From 3d0a0ccc0b9cddb48059b4dc020cd7cac0aca0ac Mon Sep 17 00:00:00 2001 From: Wunkolo <Wunkolo@gmail.com> Date: Mon, 11 Mar 2024 09:55:00 -0700 Subject: [PATCH] Refactor `Rust::Result` into `std::expected` The standard library already has support for this type as of C++23: https://en.cppreference.com/w/cpp/header/expected [This is also supported by all major compilers](https://en.cppreference.com/w/cpp/compiler_support#:~:text=Yes-,%3Cexpected%3E,-P0323R12%0AP2549R1) --- CMakeLists.txt | 3 +- include/fs/archive_base.hpp | 285 ++++--- include/fs/archive_ext_save_data.hpp | 20 +- include/fs/archive_ncch.hpp | 9 +- include/fs/archive_save_data.hpp | 14 +- include/fs/archive_sdmc.hpp | 4 +- include/fs/archive_self_ncch.hpp | 4 +- include/fs/archive_system_save_data.hpp | 4 +- include/fs/archive_user_save_data.hpp | 7 +- include/services/fs.hpp | 6 +- src/core/fs/archive_ext_save_data.cpp | 42 +- src/core/fs/archive_ncch.cpp | 60 +- src/core/fs/archive_save_data.cpp | 38 +- src/core/fs/archive_sdmc.cpp | 17 +- src/core/fs/archive_self_ncch.cpp | 11 +- src/core/fs/archive_system_save_data.cpp | 20 +- src/core/fs/archive_user_save_data.cpp | 30 +- src/core/services/fs.cpp | 153 ++-- third_party/result/LICENSE | 201 ----- third_party/result/README.md | 132 ---- third_party/result/include/result.hpp | 910 ----------------------- 21 files changed, 349 insertions(+), 1621 deletions(-) delete mode 100644 third_party/result/LICENSE delete mode 100644 third_party/result/README.md delete mode 100644 third_party/result/include/result.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 4dbe438f..5c9dd83b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,7 +6,7 @@ else() cmake_minimum_required(VERSION 3.10) endif() -set(CMAKE_CXX_STANDARD 20) +set(CMAKE_CXX_STANDARD 23) set(CMAKE_CXX_STANDARD_REQUIRED True) if(CMAKE_CXX_COMPILER_ID STREQUAL "Clang" AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 12) @@ -49,7 +49,6 @@ include_directories(third_party/imgui/) include_directories(third_party/dynarmic/src) include_directories(third_party/cryptopp/) include_directories(third_party/cityhash/include) -include_directories(third_party/result/include) include_directories(third_party/xxhash/include) include_directories(third_party/httplib) include_directories(third_party/stb) diff --git a/include/fs/archive_base.hpp b/include/fs/archive_base.hpp index 2843be68..b613a11d 100644 --- a/include/fs/archive_base.hpp +++ b/include/fs/archive_base.hpp @@ -2,26 +2,27 @@ #include <cassert> #include <cstdio> #include <cstring> +#include <expected> #include <filesystem> #include <optional> #include <string> #include <type_traits> #include <vector> + #include "helpers.hpp" #include "memory.hpp" -#include "result.hpp" #include "result/result.hpp" using Result::HorizonResult; namespace PathType { - enum : u32 { - Invalid = 0, - Empty = 1, - Binary = 2, - ASCII = 3, - UTF16 = 4, - }; + enum : u32 { + Invalid = 0, + Empty = 1, + Binary = 2, + ASCII = 3, + UTF16 = 4, + }; } namespace ArchiveID { @@ -42,83 +43,81 @@ namespace ArchiveID { UserSaveData2 = 0x567890B4, }; - static std::string toString(u32 id) { - switch (id) { - case SelfNCCH: return "SelfNCCH"; - case SaveData: return "SaveData"; - case ExtSaveData: return "ExtSaveData"; - case SharedExtSaveData: return "SharedExtSaveData"; - case SystemSaveData: return "SystemSaveData"; - case SDMC: return "SDMC"; - case SDMCWriteOnly: return "SDMC (Write-only)"; - case SavedataAndNcch: return "Savedata & NCCH (archive 0x2345678A)"; - default: return "Unknown archive"; - } - } -} + static std::string toString(u32 id) { + switch (id) { + case SelfNCCH: return "SelfNCCH"; + case SaveData: return "SaveData"; + case ExtSaveData: return "ExtSaveData"; + case SharedExtSaveData: return "SharedExtSaveData"; + case SystemSaveData: return "SystemSaveData"; + case SDMC: return "SDMC"; + case SDMCWriteOnly: return "SDMC (Write-only)"; + case SavedataAndNcch: return "Savedata & NCCH (archive 0x2345678A)"; + default: return "Unknown archive"; + } + } +} // namespace ArchiveID struct FSPath { - u32 type = PathType::Invalid; + u32 type = PathType::Invalid; - std::vector<u8> binary; // Path data for binary paths - std::string string; // Path data for ASCII paths - std::u16string utf16_string; + std::vector<u8> binary; // Path data for binary paths + std::string string; // Path data for ASCII paths + std::u16string utf16_string; - FSPath() {} + FSPath() {} - FSPath(u32 type, const std::vector<u8>& vec) : type(type) { - switch (type) { - case PathType::Binary: - binary = std::move(vec); - break; + FSPath(u32 type, const std::vector<u8>& vec) : type(type) { + switch (type) { + case PathType::Binary: binary = std::move(vec); break; - case PathType::ASCII: - string.resize(vec.size() - 1); // -1 because of the null terminator - std::memcpy(string.data(), vec.data(), vec.size() - 1); // Copy string data - break; + case PathType::ASCII: + string.resize(vec.size() - 1); // -1 because of the null terminator + std::memcpy(string.data(), vec.data(), vec.size() - 1); // Copy string data + break; - case PathType::UTF16: { - const size_t size = vec.size() / sizeof(u16) - 1; // Character count. -1 because null terminator here too - utf16_string.resize(size); - std::memcpy(utf16_string.data(), vec.data(), size * sizeof(u16)); - break; - } -; } - } + case PathType::UTF16: { + const size_t size = vec.size() / sizeof(u16) - 1; // Character count. -1 because null terminator here too + utf16_string.resize(size); + std::memcpy(utf16_string.data(), vec.data(), size * sizeof(u16)); + break; + }; + } + } }; struct FilePerms { - u32 raw; + u32 raw; - FilePerms(u32 val) : raw(val) {} - bool read() const { return (raw & 1) != 0; } - bool write() const { return (raw & 2) != 0; } - bool create() const { return (raw & 4) != 0; } + FilePerms(u32 val) : raw(val) {} + bool read() const { return (raw & 1) != 0; } + bool write() const { return (raw & 2) != 0; } + bool create() const { return (raw & 4) != 0; } }; class ArchiveBase; struct FileSession { - ArchiveBase* archive = nullptr; - FILE* fd = nullptr; // File descriptor for file sessions that require them. - FSPath path; - FSPath archivePath; - u32 priority = 0; // TODO: What does this even do - bool isOpen; + ArchiveBase* archive = nullptr; + FILE* fd = nullptr; // File descriptor for file sessions that require them. + FSPath path; + FSPath archivePath; + u32 priority = 0; // TODO: What does this even do + bool isOpen; - FileSession(ArchiveBase* archive, const FSPath& filePath, const FSPath& archivePath, FILE* fd, bool isOpen = true) : - archive(archive), path(filePath), archivePath(archivePath), fd(fd), isOpen(isOpen), priority(0) {} + FileSession(ArchiveBase* archive, const FSPath& filePath, const FSPath& archivePath, FILE* fd, bool isOpen = true) + : archive(archive), path(filePath), archivePath(archivePath), fd(fd), isOpen(isOpen), priority(0) {} - // For cloning a file session - FileSession(const FileSession& other) : archive(other.archive), path(other.path), - archivePath(other.archivePath), fd(other.fd), isOpen(other.isOpen), priority(other.priority) {} + // For cloning a file session + FileSession(const FileSession& other) + : archive(other.archive), path(other.path), archivePath(other.archivePath), fd(other.fd), isOpen(other.isOpen), priority(other.priority) {} }; struct ArchiveSession { - ArchiveBase* archive = nullptr; - FSPath path; - bool isOpen; + ArchiveBase* archive = nullptr; + FSPath path; + bool isOpen; - ArchiveSession(ArchiveBase* archive, const FSPath& filePath, bool isOpen = true) : archive(archive), path(filePath), isOpen(isOpen) {} + ArchiveSession(ArchiveBase* archive, const FSPath& filePath, bool isOpen = true) : archive(archive), path(filePath), isOpen(isOpen) {} }; struct DirectoryEntry { @@ -156,106 +155,104 @@ struct DirectorySession { using FileDescriptor = std::optional<FILE*>; class ArchiveBase { -public: - struct FormatInfo { - u32 size; // Archive size - u32 numOfDirectories; // Number of directories - u32 numOfFiles; // Number of files - bool duplicateData; // Whether to duplicate data or not - }; + public: + struct FormatInfo { + u32 size; // Archive size + u32 numOfDirectories; // Number of directories + u32 numOfFiles; // Number of files + bool duplicateData; // Whether to duplicate data or not + }; -protected: - using Handle = u32; + protected: + using Handle = u32; - static constexpr FileDescriptor NoFile = nullptr; - static constexpr FileDescriptor FileError = std::nullopt; - Memory& mem; + static constexpr FileDescriptor NoFile = nullptr; + static constexpr FileDescriptor FileError = std::nullopt; + Memory& mem; - // Returns if a specified 3DS path in UTF16 or ASCII format is safe or not - // A 3DS path is considered safe if its first character is '/' which means we're not trying to access anything outside the root of the fs - // And if it doesn't contain enough instances of ".." (Indicating "climb up a folder" in filesystems) to let the software climb up the directory tree - // And access files outside of the emulator's app data folder - template <u32 format> - bool isPathSafe(const FSPath& path) { - static_assert(format == PathType::ASCII || format == PathType::UTF16); - using String = typename std::conditional<format == PathType::UTF16, std::u16string, std::string>::type; // String type for the path - using Char = typename String::value_type; // Char type for the path + // Returns if a specified 3DS path in UTF16 or ASCII format is safe or not + // A 3DS path is considered safe if its first character is '/' which means we're not trying to access anything outside the root of the fs + // And if it doesn't contain enough instances of ".." (Indicating "climb up a folder" in filesystems) to let the software climb up the directory + // tree And access files outside of the emulator's app data folder + template <u32 format> + bool isPathSafe(const FSPath& path) { + static_assert(format == PathType::ASCII || format == PathType::UTF16); + using String = typename std::conditional<format == PathType::UTF16, std::u16string, std::string>::type; // String type for the path + using Char = typename String::value_type; // Char type for the path - String pathString, dots; - if constexpr (std::is_same<String, std::u16string>()) { - pathString = path.utf16_string; - dots = u".."; - } else { - pathString = path.string; - dots = ".."; - } + String pathString, dots; + if constexpr (std::is_same<String, std::u16string>()) { + pathString = path.utf16_string; + dots = u".."; + } else { + pathString = path.string; + dots = ".."; + } - // If the path string doesn't begin with / then that means it's accessing outside the FS root, which is invalid & unsafe - if (pathString[0] != Char('/')) return false; + // If the path string doesn't begin with / then that means it's accessing outside the FS root, which is invalid & unsafe + if (pathString[0] != Char('/')) return false; - // Counts how many folders sans the root our file is nested under. - // If it's < 0 at any point of parsing, then the path is unsafe and tries to crawl outside our file sandbox. - // If it's 0 then this is the FS root. - // If it's > 0 then we're in a subdirectory of the root. - int level = 0; + // Counts how many folders sans the root our file is nested under. + // If it's < 0 at any point of parsing, then the path is unsafe and tries to crawl outside our file sandbox. + // If it's 0 then this is the FS root. + // If it's > 0 then we're in a subdirectory of the root. + int level = 0; - // Split the string on / characters and see how many of the substrings are ".." - size_t pos = 0; - while ((pos = pathString.find(Char('/'))) != String::npos) { - String token = pathString.substr(0, pos); - pathString.erase(0, pos + 1); + // Split the string on / characters and see how many of the substrings are ".." + size_t pos = 0; + while ((pos = pathString.find(Char('/'))) != String::npos) { + String token = pathString.substr(0, pos); + pathString.erase(0, pos + 1); - if (token == dots) { - level--; - if (level < 0) return false; - } else { - level++; - } - } + if (token == dots) { + level--; + if (level < 0) return false; + } else { + level++; + } + } - return true; - } + return true; + } -public: - virtual std::string name() = 0; - virtual u64 getFreeBytes() = 0; - virtual HorizonResult createFile(const FSPath& path, u64 size) = 0; - virtual HorizonResult deleteFile(const FSPath& path) = 0; + public: + virtual std::string name() = 0; + virtual u64 getFreeBytes() = 0; + virtual HorizonResult createFile(const FSPath& path, u64 size) = 0; + virtual HorizonResult deleteFile(const FSPath& path) = 0; - virtual Rust::Result<FormatInfo, HorizonResult> getFormatInfo(const FSPath& path) { - Helpers::panic("Unimplemented GetFormatInfo for %s archive", name().c_str()); - // Return a dummy struct just to avoid the UB of not returning anything, even if we panic - return Ok(FormatInfo{ .size = 0, .numOfDirectories = 0, .numOfFiles = 0, .duplicateData = false }); - } + virtual std::expected<FormatInfo, HorizonResult> getFormatInfo(const FSPath& path) { + Helpers::panic("Unimplemented GetFormatInfo for %s archive", name().c_str()); + // Return a dummy struct just to avoid the UB of not returning anything, even if we panic + return FormatInfo{.size = 0, .numOfDirectories = 0, .numOfFiles = 0, .duplicateData = false}; + } - virtual HorizonResult createDirectory(const FSPath& path) { - Helpers::panic("Unimplemented CreateDirectory for %s archive", name().c_str()); - return Result::FS::AlreadyExists; - } + virtual HorizonResult createDirectory(const FSPath& path) { + Helpers::panic("Unimplemented CreateDirectory for %s archive", name().c_str()); + return Result::FS::AlreadyExists; + } - // Returns nullopt if opening the file failed, otherwise returns a file descriptor to it (nullptr if none is needed) - virtual FileDescriptor openFile(const FSPath& path, const FilePerms& perms) = 0; - virtual Rust::Result<ArchiveBase*, HorizonResult> openArchive(const FSPath& path) = 0; + // Returns nullopt if opening the file failed, otherwise returns a file descriptor to it (nullptr if none is needed) + virtual FileDescriptor openFile(const FSPath& path, const FilePerms& perms) = 0; + virtual std::expected<ArchiveBase*, HorizonResult> openArchive(const FSPath& path) = 0; - virtual Rust::Result<DirectorySession, HorizonResult> openDirectory(const FSPath& path) { - Helpers::panic("Unimplemented OpenDirectory for %s archive", name().c_str()); - return Err(Result::FS::FileNotFoundAlt); - } + virtual std::expected<DirectorySession, HorizonResult> openDirectory(const FSPath& path) { + Helpers::panic("Unimplemented OpenDirectory for %s archive", name().c_str()); + return std::unexpected(Result::FS::FileNotFoundAlt); + } - virtual void format(const FSPath& path, const FormatInfo& info) { - Helpers::panic("Unimplemented Format for %s archive", name().c_str()); - } + virtual void format(const FSPath& path, const FormatInfo& info) { Helpers::panic("Unimplemented Format for %s archive", name().c_str()); } - virtual HorizonResult renameFile(const FSPath& oldPath, const FSPath& newPath) { + virtual HorizonResult renameFile(const FSPath& oldPath, const FSPath& newPath) { Helpers::panic("Unimplemented RenameFile for %s archive", name().c_str()); return Result::Success; - } + } - // Read size bytes from a file starting at offset "offset" into a certain buffer in memory - // Returns the number of bytes read, or nullopt if the read failed - virtual std::optional<u32> readFile(FileSession* file, u64 offset, u32 size, u32 dataPointer) = 0; + // Read size bytes from a file starting at offset "offset" into a certain buffer in memory + // Returns the number of bytes read, or nullopt if the read failed + virtual std::optional<u32> readFile(FileSession* file, u64 offset, u32 size, u32 dataPointer) = 0; - ArchiveBase(Memory& mem) : mem(mem) {} + ArchiveBase(Memory& mem) : mem(mem) {} }; struct ArchiveResource { diff --git a/include/fs/archive_ext_save_data.hpp b/include/fs/archive_ext_save_data.hpp index 7c8c7503..5c150d9b 100644 --- a/include/fs/archive_ext_save_data.hpp +++ b/include/fs/archive_ext_save_data.hpp @@ -2,11 +2,13 @@ #include "archive_base.hpp" class ExtSaveDataArchive : public ArchiveBase { -public: - ExtSaveDataArchive(Memory& mem, const std::string& folder, bool isShared = false) : ArchiveBase(mem), - isShared(isShared), backingFolder(folder) {} + public: + ExtSaveDataArchive(Memory& mem, const std::string& folder, bool isShared = false) : ArchiveBase(mem), isShared(isShared), backingFolder(folder) {} - u64 getFreeBytes() override { Helpers::panic("ExtSaveData::GetFreeBytes unimplemented"); return 0; } + u64 getFreeBytes() override { + Helpers::panic("ExtSaveData::GetFreeBytes unimplemented"); + return 0; + } std::string name() override { return "ExtSaveData::" + backingFolder; } HorizonResult createDirectory(const FSPath& path) override; @@ -14,14 +16,14 @@ public: HorizonResult deleteFile(const FSPath& path) override; HorizonResult renameFile(const FSPath& oldPath, const FSPath& newPath) override; - Rust::Result<ArchiveBase*, HorizonResult> openArchive(const FSPath& path) override; - Rust::Result<DirectorySession, HorizonResult> openDirectory(const FSPath& path) override; + std::expected<ArchiveBase*, HorizonResult> openArchive(const FSPath& path) override; + std::expected<DirectorySession, HorizonResult> openDirectory(const FSPath& path) override; FileDescriptor openFile(const FSPath& path, const FilePerms& perms) override; std::optional<u32> readFile(FileSession* file, u64 offset, u32 size, u32 dataPointer) override; - Rust::Result<FormatInfo, HorizonResult> getFormatInfo(const FSPath& path) override { + std::expected<FormatInfo, HorizonResult> getFormatInfo(const FSPath& path) override { Helpers::warn("Stubbed ExtSaveData::GetFormatInfo"); - return Ok(FormatInfo{.size = 1_GB, .numOfDirectories = 255, .numOfFiles = 255, .duplicateData = false}); + return FormatInfo{.size = 1_GB, .numOfDirectories = 255, .numOfFiles = 255, .duplicateData = false}; } // Takes in a binary ExtSaveData path, outputs a combination of the backing folder with the low and high save entries of the path @@ -29,5 +31,5 @@ public: std::string getExtSaveDataPathFromBinary(const FSPath& path); bool isShared = false; - std::string backingFolder; // Backing folder for the archive. Can be NAND, Gamecard or SD depending on the archive path. + std::string backingFolder; // Backing folder for the archive. Can be NAND, Gamecard or SD depending on the archive path. }; \ No newline at end of file diff --git a/include/fs/archive_ncch.hpp b/include/fs/archive_ncch.hpp index 275bcd20..ebe42c8b 100644 --- a/include/fs/archive_ncch.hpp +++ b/include/fs/archive_ncch.hpp @@ -2,16 +2,19 @@ #include "archive_base.hpp" class NCCHArchive : public ArchiveBase { -public: + public: NCCHArchive(Memory& mem) : ArchiveBase(mem) {} - u64 getFreeBytes() override { Helpers::panic("NCCH::GetFreeBytes unimplemented"); return 0; } + u64 getFreeBytes() override { + Helpers::panic("NCCH::GetFreeBytes unimplemented"); + return 0; + } std::string name() override { return "NCCH"; } HorizonResult createFile(const FSPath& path, u64 size) override; HorizonResult deleteFile(const FSPath& path) override; - Rust::Result<ArchiveBase*, HorizonResult> openArchive(const FSPath& path) override; + std::expected<ArchiveBase*, HorizonResult> openArchive(const FSPath& path) override; FileDescriptor openFile(const FSPath& path, const FilePerms& perms) override; std::optional<u32> readFile(FileSession* file, u64 offset, u32 size, u32 dataPointer) override; diff --git a/include/fs/archive_save_data.hpp b/include/fs/archive_save_data.hpp index 5b1ba489..50b604c8 100644 --- a/include/fs/archive_save_data.hpp +++ b/include/fs/archive_save_data.hpp @@ -2,7 +2,7 @@ #include "archive_base.hpp" class SaveDataArchive : public ArchiveBase { -public: + public: SaveDataArchive(Memory& mem) : ArchiveBase(mem) {} u64 getFreeBytes() override { return 32_MB; } @@ -12,21 +12,19 @@ public: HorizonResult createFile(const FSPath& path, u64 size) override; HorizonResult deleteFile(const FSPath& path) override; - Rust::Result<ArchiveBase*, HorizonResult> openArchive(const FSPath& path) override; - Rust::Result<DirectorySession, HorizonResult> openDirectory(const FSPath& path) override; + std::expected<ArchiveBase*, HorizonResult> openArchive(const FSPath& path) override; + std::expected<DirectorySession, HorizonResult> openDirectory(const FSPath& path) override; FileDescriptor openFile(const FSPath& path, const FilePerms& perms) override; std::optional<u32> readFile(FileSession* file, u64 offset, u32 size, u32 dataPointer) override; void format(const FSPath& path, const FormatInfo& info) override; - Rust::Result<FormatInfo, HorizonResult> getFormatInfo(const FSPath& path) override; + std::expected<FormatInfo, HorizonResult> getFormatInfo(const FSPath& path) override; - std::filesystem::path getFormatInfoPath() { - return IOFile::getAppData() / "FormatInfo" / "SaveData.format"; - } + std::filesystem::path getFormatInfoPath() { return IOFile::getAppData() / "FormatInfo" / "SaveData.format"; } // Returns whether the cart has save data or not bool cartHasSaveData() { auto cxi = mem.getCXI(); - return (cxi != nullptr && cxi->hasSaveData()); // We need to have a CXI file with more than 0 bytes of save data + return (cxi != nullptr && cxi->hasSaveData()); // We need to have a CXI file with more than 0 bytes of save data } }; \ No newline at end of file diff --git a/include/fs/archive_sdmc.hpp b/include/fs/archive_sdmc.hpp index f63731c4..f0a13889 100644 --- a/include/fs/archive_sdmc.hpp +++ b/include/fs/archive_sdmc.hpp @@ -17,8 +17,8 @@ class SDMCArchive : public ArchiveBase { HorizonResult deleteFile(const FSPath& path) override; HorizonResult createDirectory(const FSPath& path) override; - Rust::Result<ArchiveBase*, HorizonResult> openArchive(const FSPath& path) override; - Rust::Result<DirectorySession, HorizonResult> openDirectory(const FSPath& path) override; + std::expected<ArchiveBase*, HorizonResult> openArchive(const FSPath& path) override; + std::expected<DirectorySession, HorizonResult> openDirectory(const FSPath& path) override; FileDescriptor openFile(const FSPath& path, const FilePerms& perms) override; std::optional<u32> readFile(FileSession* file, u64 offset, u32 size, u32 dataPointer) override; diff --git a/include/fs/archive_self_ncch.hpp b/include/fs/archive_self_ncch.hpp index ed882a7d..053ee4a5 100644 --- a/include/fs/archive_self_ncch.hpp +++ b/include/fs/archive_self_ncch.hpp @@ -2,7 +2,7 @@ #include "archive_base.hpp" class SelfNCCHArchive : public ArchiveBase { -public: + public: SelfNCCHArchive(Memory& mem) : ArchiveBase(mem) {} u64 getFreeBytes() override { return 0; } @@ -11,7 +11,7 @@ public: HorizonResult createFile(const FSPath& path, u64 size) override; HorizonResult deleteFile(const FSPath& path) override; - Rust::Result<ArchiveBase*, HorizonResult> openArchive(const FSPath& path) override; + std::expected<ArchiveBase*, HorizonResult> openArchive(const FSPath& path) override; FileDescriptor openFile(const FSPath& path, const FilePerms& perms) override; std::optional<u32> readFile(FileSession* file, u64 offset, u32 size, u32 dataPointer) override; diff --git a/include/fs/archive_system_save_data.hpp b/include/fs/archive_system_save_data.hpp index c4526bec..0d374238 100644 --- a/include/fs/archive_system_save_data.hpp +++ b/include/fs/archive_system_save_data.hpp @@ -16,8 +16,8 @@ class SystemSaveDataArchive : public ArchiveBase { HorizonResult createFile(const FSPath& path, u64 size) override; HorizonResult deleteFile(const FSPath& path) override; - Rust::Result<ArchiveBase*, HorizonResult> openArchive(const FSPath& path) override; - Rust::Result<DirectorySession, HorizonResult> openDirectory(const FSPath& path) override; + std::expected<ArchiveBase*, HorizonResult> openArchive(const FSPath& path) override; + std::expected<DirectorySession, HorizonResult> openDirectory(const FSPath& path) override; FileDescriptor openFile(const FSPath& path, const FilePerms& perms) override; diff --git a/include/fs/archive_user_save_data.hpp b/include/fs/archive_user_save_data.hpp index 56e2c0f6..58db296d 100644 --- a/include/fs/archive_user_save_data.hpp +++ b/include/fs/archive_user_save_data.hpp @@ -3,6 +3,7 @@ class UserSaveDataArchive : public ArchiveBase { u32 archiveID; + public: UserSaveDataArchive(Memory& mem, u32 archiveID) : ArchiveBase(mem), archiveID(archiveID) {} @@ -13,13 +14,13 @@ class UserSaveDataArchive : public ArchiveBase { HorizonResult createFile(const FSPath& path, u64 size) override; HorizonResult deleteFile(const FSPath& path) override; - Rust::Result<ArchiveBase*, HorizonResult> openArchive(const FSPath& path) override; - Rust::Result<DirectorySession, HorizonResult> openDirectory(const FSPath& path) override; + std::expected<ArchiveBase*, HorizonResult> openArchive(const FSPath& path) override; + std::expected<DirectorySession, HorizonResult> openDirectory(const FSPath& path) override; FileDescriptor openFile(const FSPath& path, const FilePerms& perms) override; std::optional<u32> readFile(FileSession* file, u64 offset, u32 size, u32 dataPointer) override; void format(const FSPath& path, const FormatInfo& info) override; - Rust::Result<FormatInfo, HorizonResult> getFormatInfo(const FSPath& path) override; + std::expected<FormatInfo, HorizonResult> getFormatInfo(const FSPath& path) override; std::filesystem::path getFormatInfoPath() { return IOFile::getAppData() / "FormatInfo" / "SaveData.format"; } diff --git a/include/services/fs.hpp b/include/services/fs.hpp index 4a613121..2f721f9d 100644 --- a/include/services/fs.hpp +++ b/include/services/fs.hpp @@ -38,8 +38,8 @@ class FSService { SystemSaveDataArchive systemSaveData; ArchiveBase* getArchiveFromID(u32 id, const FSPath& archivePath); - Rust::Result<Handle, HorizonResult> openArchiveHandle(u32 archiveID, const FSPath& path); - Rust::Result<Handle, HorizonResult> openDirectoryHandle(ArchiveBase* archive, const FSPath& path); + std::expected<Handle, HorizonResult> openArchiveHandle(u32 archiveID, const FSPath& path); + std::expected<Handle, HorizonResult> openDirectoryHandle(ArchiveBase* archive, const FSPath& path); std::optional<Handle> openFileHandle(ArchiveBase* archive, const FSPath& path, const FSPath& archivePath, const FilePerms& perms); FSPath readPath(u32 type, u32 pointer, u32 size); @@ -81,7 +81,7 @@ class FSService { // Used for set/get priority: Not sure what sort of priority this is referring to u32 priority; -public: + public: FSService(Memory& mem, Kernel& kernel, const EmulatorConfig& config) : mem(mem), saveData(mem), sharedExtSaveData_nand(mem, "../SharedFiles/NAND", true), extSaveData_sdmc(mem, "SDMC"), sdmc(mem), sdmcWriteOnly(mem, true), selfNcch(mem), ncch(mem), userSaveData1(mem, ArchiveID::UserSaveData1), diff --git a/src/core/fs/archive_ext_save_data.cpp b/src/core/fs/archive_ext_save_data.cpp index 4b57f245..b84242d7 100644 --- a/src/core/fs/archive_ext_save_data.cpp +++ b/src/core/fs/archive_ext_save_data.cpp @@ -1,21 +1,19 @@ #include "fs/archive_ext_save_data.hpp" + #include <memory> namespace fs = std::filesystem; HorizonResult ExtSaveDataArchive::createFile(const FSPath& path, u64 size) { - if (size == 0) - Helpers::panic("ExtSaveData file does not support size == 0"); + if (size == 0) Helpers::panic("ExtSaveData file does not support size == 0"); if (path.type == PathType::UTF16) { - if (!isPathSafe<PathType::UTF16>(path)) - Helpers::panic("Unsafe path in ExtSaveData::CreateFile"); + if (!isPathSafe<PathType::UTF16>(path)) Helpers::panic("Unsafe path in ExtSaveData::CreateFile"); fs::path p = IOFile::getAppData() / backingFolder; p += fs::path(path.utf16_string).make_preferred(); - if (fs::exists(p)) - return Result::FS::AlreadyExists; + if (fs::exists(p)) return Result::FS::AlreadyExists; // Create a file of size "size" by creating an empty one, seeking to size - 1 and just writing a 0 there IOFile file(p.string().c_str(), "wb"); @@ -34,8 +32,7 @@ HorizonResult ExtSaveDataArchive::createFile(const FSPath& path, u64 size) { HorizonResult ExtSaveDataArchive::deleteFile(const FSPath& path) { if (path.type == PathType::UTF16) { - if (!isPathSafe<PathType::UTF16>(path)) - Helpers::panic("Unsafe path in ExtSaveData::DeleteFile"); + if (!isPathSafe<PathType::UTF16>(path)) Helpers::panic("Unsafe path in ExtSaveData::DeleteFile"); fs::path p = IOFile::getAppData() / backingFolder; p += fs::path(path.utf16_string).make_preferred(); @@ -66,17 +63,15 @@ HorizonResult ExtSaveDataArchive::deleteFile(const FSPath& path) { FileDescriptor ExtSaveDataArchive::openFile(const FSPath& path, const FilePerms& perms) { if (path.type == PathType::UTF16) { - if (!isPathSafe<PathType::UTF16>(path)) - Helpers::panic("Unsafe path in ExtSaveData::OpenFile"); + if (!isPathSafe<PathType::UTF16>(path)) Helpers::panic("Unsafe path in ExtSaveData::OpenFile"); - if (perms.create()) - Helpers::panic("[ExtSaveData] Can't open file with create flag"); + if (perms.create()) Helpers::panic("[ExtSaveData] Can't open file with create flag"); fs::path p = IOFile::getAppData() / backingFolder; p += fs::path(path.utf16_string).make_preferred(); - if (fs::exists(p)) { // Return file descriptor if the file exists - IOFile file(p.string().c_str(), "r+b"); // According to Citra, this ignores the OpenFlags field and always opens as r+b? TODO: Check + if (fs::exists(p)) { // Return file descriptor if the file exists + IOFile file(p.string().c_str(), "r+b"); // According to Citra, this ignores the OpenFlags field and always opens as r+b? TODO: Check return file.isOpen() ? file.getHandle() : FileError; } else { return FileError; @@ -155,7 +150,7 @@ std::string ExtSaveDataArchive::getExtSaveDataPathFromBinary(const FSPath& path) return backingFolder + std::to_string(saveLow) + std::to_string(saveHigh); } -Rust::Result<ArchiveBase*, HorizonResult> ExtSaveDataArchive::openArchive(const FSPath& path) { +std::expected<ArchiveBase*, HorizonResult> ExtSaveDataArchive::openArchive(const FSPath& path) { if (path.type != PathType::Binary || path.binary.size() != 12) { Helpers::panic("ExtSaveData accessed with an invalid path in OpenArchive"); } @@ -165,34 +160,33 @@ Rust::Result<ArchiveBase*, HorizonResult> ExtSaveDataArchive::openArchive(const // fs::path formatInfopath = IOFile::getAppData() / "FormatInfo" / (getExtSaveDataPathFromBinary(path) + ".format"); // Format info not found so the archive is not formatted // if (!fs::is_regular_file(formatInfopath)) { - // return isShared ? Err(Result::FS::NotFormatted) : Err(Result::FS::NotFoundInvalid); + // return isShared ? std::unexpected(Result::FS::NotFormatted) : std::unexpected(Result::FS::NotFoundInvalid); //} - return Ok((ArchiveBase*)this); + return (ArchiveBase*)this; } -Rust::Result<DirectorySession, HorizonResult> ExtSaveDataArchive::openDirectory(const FSPath& path) { +std::expected<DirectorySession, HorizonResult> ExtSaveDataArchive::openDirectory(const FSPath& path) { if (path.type == PathType::UTF16) { - if (!isPathSafe<PathType::UTF16>(path)) - Helpers::panic("Unsafe path in ExtSaveData::OpenDirectory"); + if (!isPathSafe<PathType::UTF16>(path)) Helpers::panic("Unsafe path in ExtSaveData::OpenDirectory"); fs::path p = IOFile::getAppData() / backingFolder; p += fs::path(path.utf16_string).make_preferred(); if (fs::is_regular_file(p)) { printf("ExtSaveData: OpenArchive used with a file path"); - return Err(Result::FS::UnexpectedFileOrDir); + return std::unexpected(Result::FS::UnexpectedFileOrDir); } if (fs::is_directory(p)) { - return Ok(DirectorySession(this, p)); + return DirectorySession(this, p); } else { - return Err(Result::FS::FileNotFoundAlt); + return std::unexpected(Result::FS::FileNotFoundAlt); } } Helpers::panic("ExtSaveDataArchive::OpenDirectory: Unimplemented path type"); - return Err(Result::Success); + return std::unexpected(Result::Success); } std::optional<u32> ExtSaveDataArchive::readFile(FileSession* file, u64 offset, u32 size, u32 dataPointer) { diff --git a/src/core/fs/archive_ncch.cpp b/src/core/fs/archive_ncch.cpp index d5a4bab5..d6a165fe 100644 --- a/src/core/fs/archive_ncch.cpp +++ b/src/core/fs/archive_ncch.cpp @@ -1,9 +1,11 @@ #include "fs/archive_ncch.hpp" + +#include <algorithm> +#include <memory> + #include "fs/bad_word_list.hpp" #include "fs/country_list.hpp" #include "fs/mii_data.hpp" -#include <algorithm> -#include <memory> namespace PathType { enum : u32 { @@ -14,11 +16,7 @@ namespace PathType { }; namespace MediaType { - enum : u8 { - NAND = 0, - SD = 1, - Gamecard = 2 - }; + enum : u8 { NAND = 0, SD = 1, Gamecard = 2 }; }; HorizonResult NCCHArchive::createFile(const FSPath& path, u64 size) { @@ -36,39 +34,36 @@ FileDescriptor NCCHArchive::openFile(const FSPath& path, const FilePerms& perms) Helpers::panic("NCCHArchive::OpenFile: Invalid path"); } - const u32 media = *(u32*)&path.binary[0]; // 0 for NCCH, 1 for SaveData - if (media != 0) - Helpers::panic("NCCHArchive::OpenFile: Tried to read non-NCCH file"); + const u32 media = *(u32*)&path.binary[0]; // 0 for NCCH, 1 for SaveData + if (media != 0) Helpers::panic("NCCHArchive::OpenFile: Tried to read non-NCCH file"); // Third word of the binary path indicates what we're reading from. const u32 type = *(u32*)&path.binary[8]; - if (media == 0 && type > 2) - Helpers::panic("NCCHArchive::OpenFile: Invalid file path type"); + if (media == 0 && type > 2) Helpers::panic("NCCHArchive::OpenFile: Invalid file path type"); return NoFile; } -Rust::Result<ArchiveBase*, HorizonResult> NCCHArchive::openArchive(const FSPath& path) { +std::expected<ArchiveBase*, HorizonResult> NCCHArchive::openArchive(const FSPath& path) { if (path.type != PathType::Binary || path.binary.size() != 16) { Helpers::panic("NCCHArchive::OpenArchive: Invalid path"); } const u32 mediaType = path.binary[8]; - if (mediaType != 0) - Helpers::panic("NCCH archive. Tried to access a mediatype other than the NAND. Type: %d", mediaType); + if (mediaType != 0) Helpers::panic("NCCH archive. Tried to access a mediatype other than the NAND. Type: %d", mediaType); - return Ok((ArchiveBase*)this); + return (ArchiveBase*)this; } std::optional<u32> NCCHArchive::readFile(FileSession* file, u64 offset, u32 size, u32 dataPointer) { - const auto& path = file->path.binary; // Path of the file - const auto& archivePath = file->archivePath.binary; // Path of the archive + const auto& path = file->path.binary; // Path of the file + const auto& archivePath = file->archivePath.binary; // Path of the archive const auto mediaType = archivePath[8]; - const auto media = *(u32*)&path[0]; // 0 for NCCH, 1 for savedata + const auto media = *(u32*)&path[0]; // 0 for NCCH, 1 for savedata const auto partition = *(u32*)&path[4]; - const auto type = *(u32*)&path[8]; // Type of the path + const auto type = *(u32*)&path[8]; // Type of the path if (mediaType == MediaType::NAND) { const u32 lowProgramID = *(u32*)&archivePath[0]; @@ -87,26 +82,29 @@ std::optional<u32> NCCHArchive::readFile(FileSession* file, u64 offset, u32 size std::vector<u8> fileData; if (highProgramID == sharedDataArchive) { - if (lowProgramID == miiData) fileData = std::vector<u8>(std::begin(MII_DATA), std::end(MII_DATA)); - else if (lowProgramID == regionManifest) fileData = std::vector<u8>(std::begin(COUNTRY_LIST_DATA), std::end(COUNTRY_LIST_DATA)); + if (lowProgramID == miiData) + fileData = std::vector<u8>(std::begin(MII_DATA), std::end(MII_DATA)); + else if (lowProgramID == regionManifest) + fileData = std::vector<u8>(std::begin(COUNTRY_LIST_DATA), std::end(COUNTRY_LIST_DATA)); else if (lowProgramID == tlsRootCertificates) { Helpers::warn("Read from Shared Data archive 00010602"); return 0; - } - else Helpers::panic("[NCCH archive] Read unimplemented NAND file. ID: %08X", lowProgramID); + } else + Helpers::panic("[NCCH archive] Read unimplemented NAND file. ID: %08X", lowProgramID); } else if (highProgramID == systemDataArchive && lowProgramID == badWordList) { fileData = std::vector<u8>(std::begin(BAD_WORD_LIST_DATA), std::end(BAD_WORD_LIST_DATA)); } else { - Helpers::panic("[NCCH archive] Read from unimplemented NCCH archive file. High program ID: %08X, low ID: %08X", - highProgramID, lowProgramID); + Helpers::panic( + "[NCCH archive] Read from unimplemented NCCH archive file. High program ID: %08X, low ID: %08X", highProgramID, lowProgramID + ); } if (offset >= fileData.size()) { Helpers::panic("[NCCH archive] Out of bounds read from NAND file"); } - u32 availableBytes = u32(fileData.size() - offset); // How many bytes we can read from the file - u32 bytesRead = std::min<u32>(size, availableBytes); // Cap the amount of bytes to read if we're going to go out of bounds + u32 availableBytes = u32(fileData.size() - offset); // How many bytes we can read from the file + u32 bytesRead = std::min<u32>(size, availableBytes); // Cap the amount of bytes to read if we're going to go out of bounds for (u32 i = 0; i < bytesRead; i++) { mem.write8(dataPointer + i, fileData[offset + i]); } @@ -117,8 +115,7 @@ std::optional<u32> NCCHArchive::readFile(FileSession* file, u64 offset, u32 size } // Code below is for mediaType == 2 (gamecard). Currently unused - if (partition != 0) - Helpers::panic("[NCCH] Tried to read from non-zero partition"); + if (partition != 0) Helpers::panic("[NCCH] Tried to read from non-zero partition"); if (type == PathType::RomFS && !hasRomFS()) { Helpers::panic("Tried to read file from non-existent RomFS"); @@ -150,8 +147,7 @@ std::optional<u32> NCCHArchive::readFile(FileSession* file, u64 offset, u32 size break; } - default: - Helpers::panic("Unimplemented file path type for NCCH archive"); + default: Helpers::panic("Unimplemented file path type for NCCH archive"); } std::unique_ptr<u8[]> data(new u8[size]); diff --git a/src/core/fs/archive_save_data.cpp b/src/core/fs/archive_save_data.cpp index 0bdb9e01..0f287480 100644 --- a/src/core/fs/archive_save_data.cpp +++ b/src/core/fs/archive_save_data.cpp @@ -1,4 +1,5 @@ #include "fs/archive_save_data.hpp" + #include <algorithm> #include <memory> @@ -6,8 +7,7 @@ namespace fs = std::filesystem; HorizonResult SaveDataArchive::createFile(const FSPath& path, u64 size) { if (path.type == PathType::UTF16) { - if (!isPathSafe<PathType::UTF16>(path)) - Helpers::panic("Unsafe path in SaveData::CreateFile"); + if (!isPathSafe<PathType::UTF16>(path)) Helpers::panic("Unsafe path in SaveData::CreateFile"); fs::path p = IOFile::getAppData() / "SaveData"; p += fs::path(path.utf16_string).make_preferred(); @@ -110,16 +110,16 @@ FileDescriptor SaveDataArchive::openFile(const FSPath& path, const FilePerms& pe const char* permString = perms.write() ? "r+b" : "rb"; - if (fs::exists(p)) { // Return file descriptor if the file exists + if (fs::exists(p)) { // Return file descriptor if the file exists IOFile file(p.string().c_str(), permString); return file.isOpen() ? file.getHandle() : FileError; } else { // If the file is not found, create it if the create flag is on if (perms.create()) { - IOFile file(p.string().c_str(), "wb"); // Create file - file.close(); // Close it + IOFile file(p.string().c_str(), "wb"); // Create file + file.close(); // Close it - file.open(p.string().c_str(), permString); // Reopen with proper perms + file.open(p.string().c_str(), permString); // Reopen with proper perms return file.isOpen() ? file.getHandle() : FileError; } else { return FileError; @@ -131,7 +131,7 @@ FileDescriptor SaveDataArchive::openFile(const FSPath& path, const FilePerms& pe return FileError; } -Rust::Result<DirectorySession, HorizonResult> SaveDataArchive::openDirectory(const FSPath& path) { +std::expected<DirectorySession, HorizonResult> SaveDataArchive::openDirectory(const FSPath& path) { if (path.type == PathType::UTF16) { if (!isPathSafe<PathType::UTF16>(path)) { Helpers::panic("Unsafe path in SaveData::OpenDirectory"); @@ -142,27 +142,27 @@ Rust::Result<DirectorySession, HorizonResult> SaveDataArchive::openDirectory(con if (fs::is_regular_file(p)) { printf("SaveData: OpenDirectory used with a file path"); - return Err(Result::FS::UnexpectedFileOrDir); + return std::unexpected(Result::FS::UnexpectedFileOrDir); } if (fs::is_directory(p)) { - return Ok(DirectorySession(this, p)); + return DirectorySession(this, p); } else { - return Err(Result::FS::FileNotFoundAlt); + return std::unexpected(Result::FS::FileNotFoundAlt); } } Helpers::panic("SaveDataArchive::OpenDirectory: Unimplemented path type"); - return Err(Result::Success); + return std::unexpected(Result::Success); } -Rust::Result<ArchiveBase::FormatInfo, HorizonResult> SaveDataArchive::getFormatInfo(const FSPath& path) { +std::expected<ArchiveBase::FormatInfo, HorizonResult> SaveDataArchive::getFormatInfo(const FSPath& path) { const fs::path formatInfoPath = getFormatInfoPath(); IOFile file(formatInfoPath, "rb"); // If the file failed to open somehow, we return that the archive is not formatted if (!file.isOpen()) { - return Err(Result::FS::NotFormatted); + return std::unexpected(Result::FS::NotFormatted); } FormatInfo ret; @@ -171,10 +171,10 @@ Rust::Result<ArchiveBase::FormatInfo, HorizonResult> SaveDataArchive::getFormatI if (!success || bytesRead != sizeof(FormatInfo)) { Helpers::warn("SaveData::GetFormatInfo: Format file exists but was not properly read into the FormatInfo struct"); - return Err(Result::FS::NotFormatted); + return std::unexpected(Result::FS::NotFormatted); } - return Ok(ret); + return ret; } void SaveDataArchive::format(const FSPath& path, const ArchiveBase::FormatInfo& info) { @@ -192,19 +192,19 @@ void SaveDataArchive::format(const FSPath& path, const ArchiveBase::FormatInfo& file.close(); } -Rust::Result<ArchiveBase*, HorizonResult> SaveDataArchive::openArchive(const FSPath& path) { +std::expected<ArchiveBase*, HorizonResult> SaveDataArchive::openArchive(const FSPath& path) { if (path.type != PathType::Empty) { Helpers::panic("Unimplemented path type for SaveData archive: %d\n", path.type); - return Err(Result::FS::NotFoundInvalid); + return std::unexpected(Result::FS::NotFoundInvalid); } const fs::path formatInfoPath = getFormatInfoPath(); // Format info not found so the archive is not formatted if (!fs::is_regular_file(formatInfoPath)) { - return Err(Result::FS::NotFormatted); + return std::unexpected(Result::FS::NotFormatted); } - return Ok((ArchiveBase*)this); + return (ArchiveBase*)this; } std::optional<u32> SaveDataArchive::readFile(FileSession* file, u64 offset, u32 size, u32 dataPointer) { diff --git a/src/core/fs/archive_sdmc.cpp b/src/core/fs/archive_sdmc.cpp index 6c34de7a..b6c75b67 100644 --- a/src/core/fs/archive_sdmc.cpp +++ b/src/core/fs/archive_sdmc.cpp @@ -1,4 +1,5 @@ #include "fs/archive_sdmc.hpp" + #include <memory> namespace fs = std::filesystem; @@ -137,10 +138,10 @@ HorizonResult SDMCArchive::createDirectory(const FSPath& path) { return success ? Result::Success : Result::FS::UnexpectedFileOrDir; } -Rust::Result<DirectorySession, HorizonResult> SDMCArchive::openDirectory(const FSPath& path) { +std::expected<DirectorySession, HorizonResult> SDMCArchive::openDirectory(const FSPath& path) { if (isWriteOnly) { Helpers::warn("SDMC: OpenDirectory is not allowed in SDMC Write-Only archive"); - return Err(Result::FS::UnexpectedFileOrDir); + return std::unexpected(Result::FS::UnexpectedFileOrDir); } if (path.type == PathType::UTF16) { @@ -153,27 +154,27 @@ Rust::Result<DirectorySession, HorizonResult> SDMCArchive::openDirectory(const F if (fs::is_regular_file(p)) { printf("SDMC: OpenDirectory used with a file path"); - return Err(Result::FS::UnexpectedFileOrDir); + return std::unexpected(Result::FS::UnexpectedFileOrDir); } if (fs::is_directory(p)) { - return Ok(DirectorySession(this, p)); + return DirectorySession(this, p); } else { - return Err(Result::FS::FileNotFoundAlt); + return std::unexpected(Result::FS::FileNotFoundAlt); } } Helpers::panic("SDMCArchive::OpenDirectory: Unimplemented path type"); - return Err(Result::Success); + return std::unexpected(Result::Success); } -Rust::Result<ArchiveBase*, HorizonResult> SDMCArchive::openArchive(const FSPath& path) { +std::expected<ArchiveBase*, HorizonResult> SDMCArchive::openArchive(const FSPath& path) { // TODO: Fail here if the SD is disabled in the connfig. if (path.type != PathType::Empty) { Helpers::panic("Unimplemented path type for SDMC::OpenArchive"); } - return Ok((ArchiveBase*)this); + return (ArchiveBase*)this; } std::optional<u32> SDMCArchive::readFile(FileSession* file, u64 offset, u32 size, u32 dataPointer) { diff --git a/src/core/fs/archive_self_ncch.cpp b/src/core/fs/archive_self_ncch.cpp index 9369152d..70bba828 100644 --- a/src/core/fs/archive_self_ncch.cpp +++ b/src/core/fs/archive_self_ncch.cpp @@ -1,4 +1,5 @@ #include "fs/archive_self_ncch.hpp" + #include <memory> // The part of the NCCH archive we're trying to access. Depends on the first 4 bytes of the binary file path @@ -33,21 +34,21 @@ FileDescriptor SelfNCCHArchive::openFile(const FSPath& path, const FilePerms& pe // Where to read the file from. (https://www.3dbrew.org/wiki/Filesystem_services#SelfNCCH_File_Path_Data_Format) // We currently only know how to read from an NCCH's RomFS, ie type = 0 - const u32 type = *(u32*)&path.binary[0]; // TODO: Get rid of UB here + const u32 type = *(u32*)&path.binary[0]; // TODO: Get rid of UB here if (type != PathType::RomFS && type != PathType::ExeFS && type != PathType::UpdateRomFS) { Helpers::panic("Read from NCCH's non-RomFS & non-exeFS section! Path type: %d", type); } - return NoFile; // No file descriptor needed for RomFS + return NoFile; // No file descriptor needed for RomFS } -Rust::Result<ArchiveBase*, HorizonResult> SelfNCCHArchive::openArchive(const FSPath& path) { +std::expected<ArchiveBase*, HorizonResult> SelfNCCHArchive::openArchive(const FSPath& path) { if (path.type != PathType::Empty) { Helpers::panic("Invalid path type for SelfNCCH archive: %d\n", path.type); - return Err(Result::FS::NotFoundInvalid); + return std::unexpected(Result::FS::NotFoundInvalid); } - return Ok((ArchiveBase*)this); + return (ArchiveBase*)this; } std::optional<u32> SelfNCCHArchive::readFile(FileSession* file, u64 offset, u32 size, u32 dataPointer) { diff --git a/src/core/fs/archive_system_save_data.cpp b/src/core/fs/archive_system_save_data.cpp index 37afb431..5264cd3f 100644 --- a/src/core/fs/archive_system_save_data.cpp +++ b/src/core/fs/archive_system_save_data.cpp @@ -1,14 +1,15 @@ -#include <algorithm> #include "fs/archive_system_save_data.hpp" +#include <algorithm> + namespace fs = std::filesystem; -Rust::Result<ArchiveBase*, HorizonResult> SystemSaveDataArchive::openArchive(const FSPath& path) { +std::expected<ArchiveBase*, HorizonResult> SystemSaveDataArchive::openArchive(const FSPath& path) { if (path.type != PathType::Binary) { Helpers::panic("Unimplemented path type for SystemSaveData::OpenArchive"); } - return Ok((ArchiveBase*)this); + return (ArchiveBase*)this; } FileDescriptor SystemSaveDataArchive::openFile(const FSPath& path, const FilePerms& perms) { @@ -108,7 +109,6 @@ HorizonResult SystemSaveDataArchive::createDirectory(const FSPath& path) { } } - HorizonResult SystemSaveDataArchive::deleteFile(const FSPath& path) { if (path.type == PathType::UTF16) { if (!isPathSafe<PathType::UTF16>(path)) { @@ -142,11 +142,11 @@ HorizonResult SystemSaveDataArchive::deleteFile(const FSPath& path) { return Result::Success; } -Rust::Result<DirectorySession, HorizonResult> SystemSaveDataArchive::openDirectory(const FSPath& path) { +std::expected<DirectorySession, HorizonResult> SystemSaveDataArchive::openDirectory(const FSPath& path) { if (path.type == PathType::UTF16) { if (!isPathSafe<PathType::UTF16>(path)) { Helpers::warn("Unsafe path in SystemSaveData::OpenDirectory"); - return Err(Result::FS::FileNotFoundAlt); + return std::unexpected(Result::FS::FileNotFoundAlt); } fs::path p = IOFile::getAppData() / ".." / "SharedFiles" / "SystemSaveData"; @@ -154,16 +154,16 @@ Rust::Result<DirectorySession, HorizonResult> SystemSaveDataArchive::openDirecto if (fs::is_regular_file(p)) { printf("SystemSaveData: OpenDirectory used with a file path"); - return Err(Result::FS::UnexpectedFileOrDir); + return std::unexpected(Result::FS::UnexpectedFileOrDir); } if (fs::is_directory(p)) { - return Ok(DirectorySession(this, p)); + return DirectorySession(this, p); } else { - return Err(Result::FS::FileNotFoundAlt); + return std::unexpected(Result::FS::FileNotFoundAlt); } } Helpers::panic("SystemSaveData::OpenDirectory: Unimplemented path type"); - return Err(Result::Success); + return std::unexpected(Result::Success); } \ No newline at end of file diff --git a/src/core/fs/archive_user_save_data.cpp b/src/core/fs/archive_user_save_data.cpp index cba9bff8..20c75145 100644 --- a/src/core/fs/archive_user_save_data.cpp +++ b/src/core/fs/archive_user_save_data.cpp @@ -1,8 +1,8 @@ +#include "fs/archive_user_save_data.hpp" + #include <algorithm> #include <memory> -#include "fs/archive_user_save_data.hpp" - namespace fs = std::filesystem; HorizonResult UserSaveDataArchive::createFile(const FSPath& path, u64 size) { @@ -118,7 +118,7 @@ FileDescriptor UserSaveDataArchive::openFile(const FSPath& path, const FilePerms return FileError; } -Rust::Result<DirectorySession, HorizonResult> UserSaveDataArchive::openDirectory(const FSPath& path) { +std::expected<DirectorySession, HorizonResult> UserSaveDataArchive::openDirectory(const FSPath& path) { if (path.type == PathType::UTF16) { if (!isPathSafe<PathType::UTF16>(path)) Helpers::panic("Unsafe path in UserSaveData::OpenDirectory"); @@ -127,27 +127,27 @@ Rust::Result<DirectorySession, HorizonResult> UserSaveDataArchive::openDirectory if (fs::is_regular_file(p)) { printf("SaveData: OpenDirectory used with a file path"); - return Err(Result::FS::UnexpectedFileOrDir); + return std::unexpected(Result::FS::UnexpectedFileOrDir); } if (fs::is_directory(p)) { - return Ok(DirectorySession(this, p)); + return DirectorySession(this, p); } else { - return Err(Result::FS::FileNotFoundAlt); + return std::unexpected(Result::FS::FileNotFoundAlt); } } Helpers::panic("UserSaveDataArchive::OpenDirectory: Unimplemented path type"); - return Err(Result::Success); + return std::unexpected(Result::Success); } -Rust::Result<ArchiveBase::FormatInfo, HorizonResult> UserSaveDataArchive::getFormatInfo(const FSPath& path) { +std::expected<ArchiveBase::FormatInfo, HorizonResult> UserSaveDataArchive::getFormatInfo(const FSPath& path) { const fs::path formatInfoPath = getFormatInfoPath(); IOFile file(formatInfoPath, "rb"); // If the file failed to open somehow, we return that the archive is not formatted if (!file.isOpen()) { - return Err(Result::FS::NotFormatted); + return std::unexpected(Result::FS::NotFormatted); } FormatInfo ret; @@ -156,10 +156,10 @@ Rust::Result<ArchiveBase::FormatInfo, HorizonResult> UserSaveDataArchive::getFor if (!success || bytesRead != sizeof(FormatInfo)) { Helpers::warn("UserSaveData::GetFormatInfo: Format file exists but was not properly read into the FormatInfo struct"); - return Err(Result::FS::NotFormatted); + return std::unexpected(Result::FS::NotFormatted); } - return Ok(ret); + return ret; } void UserSaveDataArchive::format(const FSPath& path, const ArchiveBase::FormatInfo& info) { @@ -177,19 +177,19 @@ void UserSaveDataArchive::format(const FSPath& path, const ArchiveBase::FormatIn file.close(); } -Rust::Result<ArchiveBase*, HorizonResult> UserSaveDataArchive::openArchive(const FSPath& path) { +std::expected<ArchiveBase*, HorizonResult> UserSaveDataArchive::openArchive(const FSPath& path) { if (path.type != PathType::Binary) { Helpers::panic("Unimplemented path type for UserSaveData archive: %d\n", path.type); - return Err(Result::FS::NotFoundInvalid); + return std::unexpected(Result::FS::NotFoundInvalid); } const fs::path formatInfoPath = getFormatInfoPath(); // Format info not found so the archive is not formatted if (!fs::is_regular_file(formatInfoPath)) { - return Err(Result::FS::NotFormatted); + return std::unexpected(Result::FS::NotFormatted); } - return Ok((ArchiveBase*)this); + return (ArchiveBase*)this; } std::optional<u32> UserSaveDataArchive::readFile(FileSession* file, u64 offset, u32 size, u32 dataPointer) { diff --git a/src/core/services/fs.cpp b/src/core/services/fs.cpp index 2e102958..aa044a53 100644 --- a/src/core/services/fs.cpp +++ b/src/core/services/fs.cpp @@ -1,10 +1,11 @@ #include "services/fs.hpp" -#include "kernel/kernel.hpp" + #include "io_file.hpp" #include "ipc.hpp" +#include "kernel/kernel.hpp" #include "result/result.hpp" -#ifdef CreateFile // windows.h defines CreateFile & DeleteFile because of course it does. +#ifdef CreateFile // windows.h defines CreateFile & DeleteFile because of course it does. #undef CreateDirectory #undef CreateFile #undef DeleteFile @@ -47,21 +48,18 @@ namespace FSCommands { }; } -void FSService::reset() { - priority = 0; -} +void FSService::reset() { priority = 0; } // Creates directories for NAND, ExtSaveData, etc if they don't already exist. Should be executed after loading a new ROM. void FSService::initializeFilesystem() { - const auto sdmcPath = IOFile::getAppData() / "SDMC"; // Create SDMC directory + const auto sdmcPath = IOFile::getAppData() / "SDMC"; // Create SDMC directory const auto nandSharedpath = IOFile::getAppData() / ".." / "SharedFiles" / "NAND"; - const auto savePath = IOFile::getAppData() / "SaveData"; // Create SaveData - const auto formatPath = IOFile::getAppData() / "FormatInfo"; // Create folder for storing archive formatting info + const auto savePath = IOFile::getAppData() / "SaveData"; // Create SaveData + const auto formatPath = IOFile::getAppData() / "FormatInfo"; // Create folder for storing archive formatting info const auto systemSaveDataPath = IOFile::getAppData() / ".." / "SharedFiles" / "SystemSaveData"; namespace fs = std::filesystem; - if (!fs::is_directory(nandSharedpath)) { fs::create_directories(nandSharedpath); } @@ -89,25 +87,21 @@ ArchiveBase* FSService::getArchiveFromID(u32 id, const FSPath& archivePath) { case ArchiveID::SaveData: return &saveData; case ArchiveID::UserSaveData2: return &userSaveData2; - case ArchiveID::ExtSaveData: - return &extSaveData_sdmc; + case ArchiveID::ExtSaveData: return &extSaveData_sdmc; - case ArchiveID::SharedExtSaveData: - return &sharedExtSaveData_nand; + case ArchiveID::SharedExtSaveData: return &sharedExtSaveData_nand; case ArchiveID::SystemSaveData: return &systemSaveData; case ArchiveID::SDMC: return &sdmc; case ArchiveID::SDMCWriteOnly: return &sdmcWriteOnly; - case ArchiveID::SavedataAndNcch: return &ncch; // This can only access NCCH outside of FSPXI - default: - Helpers::panic("Unknown archive. ID: %d\n", id); - return nullptr; + case ArchiveID::SavedataAndNcch: return &ncch; // This can only access NCCH outside of FSPXI + default: Helpers::panic("Unknown archive. ID: %d\n", id); return nullptr; } } std::optional<Handle> FSService::openFileHandle(ArchiveBase* archive, const FSPath& path, const FSPath& archivePath, const FilePerms& perms) { FileDescriptor opened = archive->openFile(path, perms); - if (opened.has_value()) { // If opened doesn't have a value, we failed to open the file + if (opened.has_value()) { // If opened doesn't have a value, we failed to open the file auto handle = kernel.makeObject(KernelObjectType::File); auto& file = kernel.getObjects()[handle]; @@ -119,37 +113,36 @@ std::optional<Handle> FSService::openFileHandle(ArchiveBase* archive, const FSPa } } -Rust::Result<Handle, Result::HorizonResult> FSService::openDirectoryHandle(ArchiveBase* archive, const FSPath& path) { - Rust::Result<DirectorySession, Result::HorizonResult> opened = archive->openDirectory(path); - if (opened.isOk()) { // If opened doesn't have a value, we failed to open the directory +std::expected<Handle, Result::HorizonResult> FSService::openDirectoryHandle(ArchiveBase* archive, const FSPath& path) { + std::expected<DirectorySession, Result::HorizonResult> opened = archive->openDirectory(path); + if (opened) { // If opened doesn't have a value, we failed to open the directory auto handle = kernel.makeObject(KernelObjectType::Directory); auto& object = kernel.getObjects()[handle]; - object.data = new DirectorySession(opened.unwrap()); + object.data = new DirectorySession(opened.value()); - return Ok(handle); + return handle; } else { - return Err(opened.unwrapErr()); + return std::unexpected(opened.error()); } } -Rust::Result<Handle, Result::HorizonResult> FSService::openArchiveHandle(u32 archiveID, const FSPath& path) { +std::expected<Handle, Result::HorizonResult> FSService::openArchiveHandle(u32 archiveID, const FSPath& path) { ArchiveBase* archive = getArchiveFromID(archiveID, path); if (archive == nullptr) [[unlikely]] { Helpers::panic("OpenArchive: Tried to open unknown archive %d.", archiveID); - return Err(Result::FS::NotFormatted); + return std::unexpected(Result::FS::NotFormatted); } - Rust::Result<ArchiveBase*, Result::HorizonResult> res = archive->openArchive(path); - if (res.isOk()) { + std::expected<ArchiveBase*, Result::HorizonResult> res = archive->openArchive(path); + if (res.has_value()) { auto handle = kernel.makeObject(KernelObjectType::Archive); auto& archiveObject = kernel.getObjects()[handle]; - archiveObject.data = new ArchiveSession(res.unwrap(), path); + archiveObject.data = new ArchiveSession(res.value(), path); - return Ok(handle); - } - else { - return Err(res.unwrapErr()); + return handle; + } else { + return std::unexpected(res.error()); } } @@ -157,8 +150,7 @@ FSPath FSService::readPath(u32 type, u32 pointer, u32 size) { std::vector<u8> data; data.resize(size); - for (u32 i = 0; i < size; i++) - data[i] = mem.read8(pointer + i); + for (u32 i = 0; i < size; i++) data[i] = mem.read8(pointer + i); return FSPath(type, data); } @@ -217,7 +209,7 @@ void FSService::initializeWithSdkVersion(u32 messagePointer) { } void FSService::closeArchive(u32 messagePointer) { - const Handle handle = static_cast<u32>(mem.read64(messagePointer + 4)); // TODO: archive handles should be 64-bit + const Handle handle = static_cast<u32>(mem.read64(messagePointer + 4)); // TODO: archive handles should be 64-bit const auto object = kernel.getObject(handle, KernelObjectType::Archive); log("FSService::CloseArchive(handle = %X)\n", handle); @@ -241,14 +233,14 @@ void FSService::openArchive(u32 messagePointer) { auto archivePath = readPath(archivePathType, archivePathPointer, archivePathSize); log("FS::OpenArchive(archive ID = %d, archive path type = %d)\n", archiveID, archivePathType); - Rust::Result<Handle, Result::HorizonResult> res = openArchiveHandle(archiveID, archivePath); + std::expected<Handle, Result::HorizonResult> res = openArchiveHandle(archiveID, archivePath); mem.write32(messagePointer, IPC::responseHeader(0x80C, 3, 0)); - if (res.isOk()) { + if (res.has_value()) { mem.write32(messagePointer + 4, Result::Success); - mem.write64(messagePointer + 8, res.unwrap()); + mem.write64(messagePointer + 8, res.value()); } else { - log("FS::OpenArchive: Failed to open archive with id = %d. Error %08X\n", archiveID, (u32)res.unwrapErr()); - mem.write32(messagePointer + 4, res.unwrapErr()); + log("FS::OpenArchive: Failed to open archive with id = %d. Error %08X\n", archiveID, (u32)res.error()); + mem.write32(messagePointer + 4, res.error()); mem.write64(messagePointer + 8, 0); } } @@ -283,7 +275,7 @@ void FSService::openFile(u32 messagePointer) { mem.write32(messagePointer + 4, Result::FS::FileNotFound); } else { mem.write32(messagePointer + 4, Result::Success); - mem.write32(messagePointer + 8, 0x10); // "Move handle descriptor" + mem.write32(messagePointer + 8, 0x10); // "Move handle descriptor" mem.write32(messagePointer + 12, handle.value()); } } @@ -330,12 +322,12 @@ void FSService::openDirectory(u32 messagePointer) { auto dir = openDirectoryHandle(archive, dirPath); mem.write32(messagePointer, IPC::responseHeader(0x80B, 1, 2)); - if (dir.isOk()) { + if (dir.has_value()) { mem.write32(messagePointer + 4, Result::Success); - mem.write32(messagePointer + 12, dir.unwrap()); + mem.write32(messagePointer + 12, dir.value()); } else { printf("FS::OpenDirectory failed\n"); - mem.write32(messagePointer + 4, static_cast<u32>(dir.unwrapErr())); + mem.write32(messagePointer + 4, static_cast<u32>(dir.error())); } } @@ -360,11 +352,11 @@ void FSService::openFileDirectly(u32 messagePointer) { auto filePath = readPath(filePathType, filePathPointer, filePathSize); const FilePerms perms(openFlags); - Rust::Result<ArchiveBase*, Result::HorizonResult> res = archive->openArchive(archivePath); - if (res.isErr()) [[unlikely]] { + std::expected<ArchiveBase*, Result::HorizonResult> res = archive->openArchive(archivePath); + if (!res.has_value()) [[unlikely]] { Helpers::panic("OpenFileDirectly: Failed to open archive with given path"); } - archive = res.unwrap(); + archive = res.value(); std::optional<Handle> handle = openFileHandle(archive, filePath, archivePath, perms); mem.write32(messagePointer, IPC::responseHeader(0x803, 1, 2)); @@ -451,18 +443,18 @@ void FSService::getFormatInfo(u32 messagePointer) { } mem.write32(messagePointer, IPC::responseHeader(0x845, 5, 0)); - Rust::Result<ArchiveBase::FormatInfo, Result::HorizonResult> res = archive->getFormatInfo(path); + std::expected<ArchiveBase::FormatInfo, Result::HorizonResult> res = archive->getFormatInfo(path); // If the FormatInfo was returned, write them to the output buffer. Otherwise, write an error code. - if (res.isOk()) { - ArchiveBase::FormatInfo info = res.unwrap(); + if (res.has_value()) { + ArchiveBase::FormatInfo info = res.value(); mem.write32(messagePointer + 4, Result::Success); mem.write32(messagePointer + 8, info.size); mem.write32(messagePointer + 12, info.numOfDirectories); mem.write32(messagePointer + 16, info.numOfFiles); mem.write8(messagePointer + 20, info.duplicateData ? 1 : 0); } else { - mem.write32(messagePointer + 4, static_cast<u32>(res.unwrapErr())); + mem.write32(messagePointer + 4, static_cast<u32>(res.error())); } } @@ -470,8 +462,7 @@ void FSService::formatSaveData(u32 messagePointer) { log("FS::FormatSaveData\n"); const u32 archiveID = mem.read32(messagePointer + 4); - if (archiveID != ArchiveID::SaveData) - Helpers::panic("FS::FormatSaveData: Archive is not SaveData"); + if (archiveID != ArchiveID::SaveData) Helpers::panic("FS::FormatSaveData: Archive is not SaveData"); // Read path and path info const u32 pathType = mem.read32(messagePointer + 8); @@ -481,21 +472,15 @@ void FSService::formatSaveData(u32 messagePointer) { // Size of a block. Seems to always be 0x200 const u32 blockSize = mem.read32(messagePointer + 16); - if (blockSize != 0x200 && blockSize != 0x1000) - Helpers::panic("FS::FormatSaveData: Invalid SaveData block size"); + if (blockSize != 0x200 && blockSize != 0x1000) Helpers::panic("FS::FormatSaveData: Invalid SaveData block size"); - const u32 directoryNum = mem.read32(messagePointer + 20); // Max number of directories - const u32 fileNum = mem.read32(messagePointer + 24); // Max number of files - const u32 directoryBucketNum = mem.read32(messagePointer + 28); // Not sure what a directory bucket is...? - const u32 fileBucketNum = mem.read32(messagePointer + 32); // Same here + const u32 directoryNum = mem.read32(messagePointer + 20); // Max number of directories + const u32 fileNum = mem.read32(messagePointer + 24); // Max number of files + const u32 directoryBucketNum = mem.read32(messagePointer + 28); // Not sure what a directory bucket is...? + const u32 fileBucketNum = mem.read32(messagePointer + 32); // Same here const bool duplicateData = mem.read8(messagePointer + 36) != 0; - ArchiveBase::FormatInfo info { - .size = blockSize * 0x200, - .numOfDirectories = directoryNum, - .numOfFiles = fileNum, - .duplicateData = duplicateData - }; + ArchiveBase::FormatInfo info{.size = blockSize * 0x200, .numOfDirectories = directoryNum, .numOfFiles = fileNum, .duplicateData = duplicateData}; saveData.format(path, info); @@ -512,8 +497,8 @@ void FSService::deleteExtSaveData(u32 messagePointer) { log("FS::DeleteExtSaveData (media type = %d, saveID = %llx) (stubbed)\n", mediaType, saveID); mem.write32(messagePointer, IPC::responseHeader(0x0852, 1, 0)); - // TODO: We can't properly implement this yet until we properly support title/save IDs. We will stub this and insert a warning for now. Required for Planet Robobot - // When we properly implement it, it will just be a recursive directory deletion + // TODO: We can't properly implement this yet until we properly support title/save IDs. We will stub this and insert a warning for now. Required + // for Planet Robobot When we properly implement it, it will just be a recursive directory deletion mem.write32(messagePointer + 4, Result::Success); } @@ -521,7 +506,8 @@ void FSService::createExtSaveData(u32 messagePointer) { Helpers::warn("Stubbed call to FS::CreateExtSaveData!"); // First 4 words of parameters are the ExtSaveData info // https://www.3dbrew.org/wiki/Filesystem_services#ExtSaveDataInfo - // This creates the ExtSaveData with the specified saveid in the specified media type. It stores the SMDH as "icon" in the root of the created directory. + // This creates the ExtSaveData with the specified saveid in the specified media type. It stores the SMDH as "icon" in the root of the created + // directory. const u8 mediaType = mem.read8(messagePointer + 4); const u64 saveID = mem.read64(messagePointer + 8); const u32 numOfDirectories = mem.read32(messagePointer + 20); @@ -541,18 +527,13 @@ void FSService::formatThisUserSaveData(u32 messagePointer) { log("FS::FormatThisUserSaveData\n"); const u32 blockSize = mem.read32(messagePointer + 4); - const u32 directoryNum = mem.read32(messagePointer + 8); // Max number of directories - const u32 fileNum = mem.read32(messagePointer + 12); // Max number of files - const u32 directoryBucketNum = mem.read32(messagePointer + 16); // Not sure what a directory bucket is...? - const u32 fileBucketNum = mem.read32(messagePointer + 20); // Same here + const u32 directoryNum = mem.read32(messagePointer + 8); // Max number of directories + const u32 fileNum = mem.read32(messagePointer + 12); // Max number of files + const u32 directoryBucketNum = mem.read32(messagePointer + 16); // Not sure what a directory bucket is...? + const u32 fileBucketNum = mem.read32(messagePointer + 20); // Same here const bool duplicateData = mem.read8(messagePointer + 24) != 0; - ArchiveBase::FormatInfo info { - .size = blockSize * 0x200, - .numOfDirectories = directoryNum, - .numOfFiles = fileNum, - .duplicateData = duplicateData - }; + ArchiveBase::FormatInfo info{.size = blockSize * 0x200, .numOfDirectories = directoryNum, .numOfFiles = fileNum, .duplicateData = duplicateData}; FSPath emptyPath; mem.write32(messagePointer, IPC::responseHeader(0x080F, 1, 0)); @@ -578,18 +559,16 @@ void FSService::controlArchive(u32 messagePointer) { } switch (action) { - case 0: // Commit save data changes. Shouldn't need us to do anything + case 0: // Commit save data changes. Shouldn't need us to do anything mem.write32(messagePointer + 4, Result::Success); break; - case 1: // Retrieves a file's last-modified timestamp. Seen in DDLC, stubbed for the moment + case 1: // Retrieves a file's last-modified timestamp. Seen in DDLC, stubbed for the moment Helpers::warn("FS::ControlArchive: Tried to retrieve a file's last-modified timestamp"); mem.write32(messagePointer + 4, Result::Success); break; - default: - Helpers::panic("Unimplemented action for ControlArchive (action = %X)\n", action); - break; + default: Helpers::panic("Unimplemented action for ControlArchive (action = %X)\n", action); break; } } @@ -673,9 +652,9 @@ void FSService::getThisSaveDataSecureValue(u32 messagePointer) { mem.write32(messagePointer, IPC::responseHeader(0x86F, 1, 0)); mem.write32(messagePointer + 4, Result::Success); - mem.write8(messagePointer + 8, 0); // Secure value does not exist - mem.write8(messagePointer + 12, 1); // TODO: What is this? - mem.write64(messagePointer + 16, 0); // Secure value + mem.write8(messagePointer + 8, 0); // Secure value does not exist + mem.write8(messagePointer + 12, 1); // TODO: What is this? + mem.write64(messagePointer + 16, 0); // Secure value } void FSService::setThisSaveDataSecureValue(u32 messagePointer) { diff --git a/third_party/result/LICENSE b/third_party/result/LICENSE deleted file mode 100644 index 6f4e91e3..00000000 --- a/third_party/result/LICENSE +++ /dev/null @@ -1,201 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "{}" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright {2016} {Mathieu Stefani} - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. diff --git a/third_party/result/README.md b/third_party/result/README.md deleted file mode 100644 index 7f588045..00000000 --- a/third_party/result/README.md +++ /dev/null @@ -1,132 +0,0 @@ -# Result -This is an adaption of [https://github.com/oktal/result](https://github.com/oktal/result). Make sure to support the original library! - -## Overview - -`Result<T, E>` is a template type that can be used to return and propage errors. It can be used to replace -exceptions in context where they are not allowed or too slow to be used. `Result<T, E>` is an algebraic data -type of `Ok(T)` that represents success and `Err(E)` representing an error. - -Design of this class has been mainly inspired by Rust's [std::result](https://doc.rust-lang.org/std/result/) - -``` - -struct Request { -}; - -struct Error { - - enum class Kind { - Timeout, - Invalid, - TooLong - } - - Error(Kind kind, std::string text); - - Kind kind; - std::string text; -}; - -Result<Request, Error> parseRequest(const std::string& payload) { - if (payload.size() > 512) return Err(Error(Kind::TooLong, "Request exceeded maximum allowed size (512 bytes)")); - - Request request; - return Ok(request); -} - -std::string payload = receivePayload(); -auto request = parseRequest(payload).expect("Failed to parse request"); -``` - -To return a successfull `Result`, use the `Ok()` function. To return an error one, use the `Err()` function. - -## Extract and unwrap - -To extract the value from a `Result<T, E>` type, you can use the `expect()` function that will yield the value -of an `Ok(T)` or terminate the program with an error message passed as a parameter. - -``` -Result<uint32_t, uint32_t> r1 = Ok(3u); - -auto val = r1.expect("Failed to retrieve the value"); -assert(val == 3); -``` - -`unwrap()` can also be used to extract the value of a `Result`, yielding the value of an `Ok(T)` value or terminating -the program otherwise: - -``` -Result<uint32_t, uint32_t> r1 = Ok(3u); - -auto val = r1.unwrap(); -assert(val == 3); -``` - -Instead a terminating the program, `unwrapOr` can be used to return a default value for an `Err(E)` Result: - -``` -Result<uint32_t, uint32_t> r1 = Err(9u); - -auto val = r1.unwrapOr(0); -assert(val == 0); -``` - -## Map and bind - -To transform (or map) a `Result<T, E>` to a `Result<U, E>`, `Result` provides a `map` member function. -`map` will apply a function to a contained `Ok(T)` value and will return the result of the transformation, -and will leave an `Err(E)` untouched: - -``` -std::string stringify(int val) { return std::to_string(val); } - -Result<uint32_t, uint32_t> r1 = Ok(2u); -auto r2 = r1.map(stringify); // Maps a Result<uint32_t, uint32_t> to Result<std::string, uint32_t> - -assert(r2.unwrap(), "2"); -``` - -Note that `map` should return a simple value and not a `Result<U, E>`. A function returning nothing (`void`) -applied to a `Result<T, E>` will yield a `Result<void, E>`. - -To map a function to a contained `Err(E)` value, use the `mapError` function. - -To *bind* a `Result<T, E>` to a `Result<U, E>`, you can use the `andThen` member function: - -``` -Result<uint32_t, uint32_t> square(uint32_t val) { return Ok(val * val); } - -Result<uint32_t, uint32_t> r1 = Ok(3u); -auto r2 = r1.andThen(square); - -assert(r2.unwrap(), 9); -``` - -Use `orElse` to apply a function to a contained `Err(E)` value: - -``` -Result<uint32_t, uint32_t> identity(uint32_t val) { return Ok(val); } - -Result<uint32_t, uint32_t> r1 = Err(3u); -assert(r1.andThen(identity).orElse(square).unwrap(), 9); -``` - -## The TRY macro - -Like Rust, a `TRY` macro is also provided that comes in handy when writing code that calls a lot of functions returning a `Result`. - -the `TRY` macro will simply call its argument and short-cirtcuit the function returning an `Err(E)` if the operation returned an error `Result`: - -``` -Result<void, IoError> copy(int srcFd, const char* dstFile) { - - auto fd = TRY(open(dstFile)); - auto data = TRY(read(srcFd)); - TRY(write(fd, data)); - - return Ok(); -} -``` - -Note that this macro uses a special extension called *compound statement* only supported by gcc and clang diff --git a/third_party/result/include/result.hpp b/third_party/result/include/result.hpp deleted file mode 100644 index b75dee90..00000000 --- a/third_party/result/include/result.hpp +++ /dev/null @@ -1,910 +0,0 @@ -/* - Mathieu Stefani, 03 mai 2016 - - This header provides a Result type that can be used to replace exceptions in code - that has to handle error. - - Result<T, E> can be used to return and propagate an error to the caller. Result<T, E> is an algebraic - data type that can either Ok(T) to represent success or Err(E) to represent an error. -*/ - -#pragma once - -#include <iostream> -#include <functional> -#include <type_traits> - -namespace types { - template<typename T> - struct Ok { - Ok(const T& val) : val(val) { } - Ok(T&& val) : val(std::move(val)) { } - - T val; - }; - - template<> - struct Ok<void> { }; - - template<typename E> - struct Err { - Err(const E& val) : val(val) { } - Err(E&& val) : val(std::move(val)) { } - - E val; - }; -} - -template<typename T, typename CleanT = typename std::decay<T>::type> -types::Ok<CleanT> Ok(T&& val) { - return types::Ok<CleanT>(std::forward<T>(val)); -} - -inline types::Ok<void> Ok() { - return types::Ok<void>(); -} - -template<typename E, typename CleanE = typename std::decay<E>::type> -types::Err<CleanE> Err(E&& val) { - return types::Err<CleanE>(std::forward<E>(val)); -} - -namespace Rust { -template<typename T, typename E> struct Result; -} - -namespace details { - -template<typename ...> struct void_t { typedef void type; }; - -namespace impl { - template<typename Func> struct result_of; - - template<typename Ret, typename Cls, typename... Args> - struct result_of<Ret (Cls::*)(Args...)> : public result_of<Ret (Args...)> { }; - - template<typename Ret, typename... Args> - struct result_of<Ret (Args...)> { - typedef Ret type; - }; -} - -template<typename Func> -struct result_of : public impl::result_of<decltype(&Func::operator())> { }; - -template<typename Ret, typename Cls, typename... Args> -struct result_of<Ret (Cls::*) (Args...) const> { - typedef Ret type; -}; - -template<typename Ret, typename... Args> -struct result_of<Ret (*)(Args...)> { - typedef Ret type; -}; - -template<typename R> -struct ResultOkType { typedef typename std::decay<R>::type type; }; - -template<typename T, typename E> -struct ResultOkType<Rust::Result<T, E>> { - typedef T type; -}; - -template<typename R> -struct ResultErrType { typedef R type; }; - -template<typename T, typename E> -struct ResultErrType<Rust::Result<T, E>> { - typedef typename std::remove_reference<E>::type type; -}; - -template<typename R> struct IsResult : public std::false_type { }; -template<typename T, typename E> -struct IsResult<Rust::Result<T, E>> : public std::true_type { }; - -namespace ok { - -namespace impl { - -template<typename T> struct Map; - -template<typename Ret, typename Cls, typename Arg> -struct Map<Ret (Cls::*)(Arg) const> : public Map<Ret (Arg)> { }; - -template<typename Ret, typename Cls, typename Arg> -struct Map<Ret (Cls::*)(Arg)> : public Map<Ret (Arg)> { }; - -// General implementation -template<typename Ret, typename Arg> -struct Map<Ret (Arg)> { - - static_assert(!IsResult<Ret>::value, - "Can not map a callback returning a Result, use andThen instead"); - - template<typename T, typename E, typename Func> - static Rust::Result<Ret, E> map(const Rust::Result<T, E>& result, Func func) { - - static_assert( - std::is_same<T, Arg>::value || - std::is_convertible<T, Arg>::value, - "Incompatible types detected"); - - if (result.isOk()) { - auto res = func(result.storage().template get<T>()); - return types::Ok<Ret>(std::move(res)); - } - - return types::Err<E>(result.storage().template get<E>()); - } -}; - -// Specialization for callback returning void -template<typename Arg> -struct Map<void (Arg)> { - - template<typename T, typename E, typename Func> - static Rust::Result<void, E> map(const Rust::Result<T, E>& result, Func func) { - - if (result.isOk()) { - func(result.storage().template get<T>()); - return types::Ok<void>(); - } - - return types::Err<E>(result.storage().template get<E>()); - } -}; - -// Specialization for a void Result -template<typename Ret> -struct Map<Ret (void)> { - - template<typename T, typename E, typename Func> - static Rust::Result<Ret, E> map(const Rust::Result<T, E>& result, Func func) { - static_assert(std::is_same<T, void>::value, - "Can not map a void callback on a non-void Result"); - - if (result.isOk()) { - auto ret = func(); - return types::Ok<Ret>(std::move(ret)); - } - - return types::Err<E>(result.storage().template get<E>()); - } -}; - -// Specialization for callback returning void on a void Result -template<> -struct Map<void (void)> { - - template<typename T, typename E, typename Func> - static Rust::Result<void, E> map(const Rust::Result<T, E>& result, Func func) { - static_assert(std::is_same<T, void>::value, - "Can not map a void callback on a non-void Result"); - - if (result.isOk()) { - func(); - return types::Ok<void>(); - } - - return types::Err<E>(result.storage().template get<E>()); - } -}; - -// General specialization for a callback returning a Result -template<typename U, typename E, typename Arg> -struct Map<Rust::Result<U, E> (Arg)> { - - template<typename T, typename Func> - static Rust::Result<U, E> map(const Rust::Result<T, E>& result, Func func) { - static_assert( - std::is_same<T, Arg>::value || - std::is_convertible<T, Arg>::value, - "Incompatible types detected"); - - if (result.isOk()) { - auto res = func(result.storage().template get<T>()); - return res; - } - - return types::Err<E>(result.storage().template get<E>()); - } -}; - -// Specialization for a void callback returning a Result -template<typename U, typename E> -struct Map<Rust::Result<U, E> (void)> { - - template<typename T, typename Func> - static Rust::Result<U, E> map(const Rust::Result<T, E>& result, Func func) { - static_assert(std::is_same<T, void>::value, "Can not call a void-callback on a non-void Result"); - - if (result.isOk()) { - auto res = func(); - return res; - } - - return types::Err<E>(result.storage().template get<E>()); - } - -}; - -} // namespace impl - -template<typename Func> struct Map : public impl::Map<decltype(&Func::operator())> { }; - -template<typename Ret, typename... Args> -struct Map<Ret (*) (Args...)> : public impl::Map<Ret (Args...)> { }; - -template<typename Ret, typename Cls, typename... Args> -struct Map<Ret (Cls::*) (Args...)> : public impl::Map<Ret (Args...)> { }; - -template<typename Ret, typename Cls, typename... Args> -struct Map<Ret (Cls::*) (Args...) const> : public impl::Map<Ret (Args...)> { }; - -template<typename Ret, typename... Args> -struct Map<std::function<Ret (Args...)>> : public impl::Map<Ret (Args...)> { }; - -} // namespace ok - - -namespace err { - -namespace impl { - -template<typename T> struct Map; - -template<typename Ret, typename Cls, typename Arg> -struct Map<Ret (Cls::*)(Arg) const> { - - static_assert(!IsResult<Ret>::value, - "Can not map a callback returning a Result, use orElse instead"); - - template<typename T, typename E, typename Func> - static Rust::Result<T, Ret> map(const Rust::Result<T, E>& result, Func func) { - if (result.isErr()) { - auto res = func(result.storage().template get<E>()); - return types::Err<Ret>(res); - } - - return types::Ok<T>(result.storage().template get<T>()); - } - - template<typename E, typename Func> - static Rust::Result<void, Ret> map(const Rust::Result<void, E>& result, Func func) { - if (result.isErr()) { - auto res = func(result.storage().template get<E>()); - return types::Err<Ret>(res); - } - - return types::Ok<void>(); - } - - -}; - -} // namespace impl - -template<typename Func> struct Map : public impl::Map<decltype(&Func::operator())> { }; - -} // namespace err; - -namespace And { - -namespace impl { - - template<typename Func> struct Then; - - template<typename Ret, typename... Args> - struct Then<Ret (*)(Args...)> : public Then<Ret (Args...)> { }; - - template<typename Ret, typename Cls, typename... Args> - struct Then<Ret (Cls::*)(Args...)> : public Then<Ret (Args...)> { }; - - template<typename Ret, typename Cls, typename... Args> - struct Then<Ret (Cls::*)(Args...) const> : public Then<Ret (Args...)> { }; - - template<typename Ret, typename Arg> - struct Then<Ret (Arg)> { - static_assert(std::is_same<Ret, void>::value, - "then() should not return anything, use map() instead"); - - template<typename T, typename E, typename Func> - static Rust::Result<T, E> then(const Rust::Result<T, E>& result, Func func) { - if (result.isOk()) { - func(result.storage().template get<T>()); - } - return result; - } - }; - - template<typename Ret> - struct Then<Ret (void)> { - static_assert(std::is_same<Ret, void>::value, - "then() should not return anything, use map() instead"); - - template<typename T, typename E, typename Func> - static Rust::Result<T, E> then(const Rust::Result<T, E>& result, Func func) { - static_assert(std::is_same<T, void>::value, "Can not call a void-callback on a non-void Result"); - - if (result.isOk()) { - func(); - } - - return result; - } - }; - - -} // namespace impl - -template<typename Func> -struct Then : public impl::Then<decltype(&Func::operator())> { }; - -template<typename Ret, typename... Args> -struct Then<Ret (*) (Args...)> : public impl::Then<Ret (Args...)> { }; - -template<typename Ret, typename Cls, typename... Args> -struct Then<Ret (Cls::*)(Args...)> : public impl::Then<Ret (Args...)> { }; - -template<typename Ret, typename Cls, typename... Args> -struct Then<Ret (Cls::*)(Args...) const> : public impl::Then<Ret (Args...)> { }; - -} // namespace And - -namespace Or { - -namespace impl { - - template<typename Func> struct Else; - - template<typename Ret, typename... Args> - struct Else<Ret (*)(Args...)> : public Else<Ret (Args...)> { }; - - template<typename Ret, typename Cls, typename... Args> - struct Else<Ret (Cls::*)(Args...)> : public Else<Ret (Args...)> { }; - - template<typename Ret, typename Cls, typename... Args> - struct Else<Ret (Cls::*)(Args...) const> : public Else<Ret (Args...)> { }; - - template<typename T, typename F, typename Arg> - struct Else<Rust::Result<T, F> (Arg)> { - - template<typename E, typename Func> - static Rust::Result<T, F> orElse(const Rust::Result<T, E>& result, Func func) { - static_assert( - std::is_same<E, Arg>::value || - std::is_convertible<E, Arg>::value, - "Incompatible types detected"); - - if (result.isErr()) { - auto res = func(result.storage().template get<E>()); - return res; - } - - return types::Ok<T>(result.storage().template get<T>()); - } - - template<typename E, typename Func> - static Rust::Result<void, F> orElse(const Rust::Result<void, E>& result, Func func) { - if (result.isErr()) { - auto res = func(result.storage().template get<E>()); - return res; - } - - return types::Ok<void>(); - } - - }; - - template<typename T, typename F> - struct Else<Rust::Result<T, F> (void)> { - - template<typename E, typename Func> - static Rust::Result<T, F> orElse(const Rust::Result<T, E>& result, Func func) { - static_assert(std::is_same<T, void>::value, - "Can not call a void-callback on a non-void Result"); - - if (result.isErr()) { - auto res = func(); - return res; - } - - return types::Ok<T>(result.storage().template get<T>()); - } - - template<typename E, typename Func> - static Rust::Result<void, F> orElse(const Rust::Result<void, E>& result, Func func) { - if (result.isErr()) { - auto res = func(); - return res; - } - - return types::Ok<void>(); - } - - }; - -} // namespace impl - -template<typename Func> -struct Else : public impl::Else<decltype(&Func::operator())> { }; - -template<typename Ret, typename... Args> -struct Else<Ret (*) (Args...)> : public impl::Else<Ret (Args...)> { }; - -template<typename Ret, typename Cls, typename... Args> -struct Else<Ret (Cls::*)(Args...)> : public impl::Else<Ret (Args...)> { }; - -template<typename Ret, typename Cls, typename... Args> -struct Else<Ret (Cls::*)(Args...) const> : public impl::Else<Ret (Args...)> { }; - -} // namespace Or - -namespace Other { - -namespace impl { - - template<typename Func> struct Wise; - - template<typename Ret, typename... Args> - struct Wise<Ret (*)(Args...)> : public Wise<Ret (Args...)> { }; - - template<typename Ret, typename Cls, typename... Args> - struct Wise<Ret (Cls::*)(Args...)> : public Wise<Ret (Args...)> { }; - - template<typename Ret, typename Cls, typename... Args> - struct Wise<Ret (Cls::*)(Args...) const> : public Wise<Ret (Args...)> { }; - - template<typename Ret, typename Arg> - struct Wise<Ret (Arg)> { - - template<typename T, typename E, typename Func> - static Rust::Result<T, E> otherwise(const Rust::Result<T, E>& result, Func func) { - static_assert( - std::is_same<E, Arg>::value || - std::is_convertible<E, Arg>::value, - "Incompatible types detected"); - - static_assert(std::is_same<Ret, void>::value, - "callback should not return anything, use mapError() for that"); - - if (result.isErr()) { - func(result.storage().template get<E>()); - } - return result; - } - - }; - -} // namespace impl - -template<typename Func> -struct Wise : public impl::Wise<decltype(&Func::operator())> { }; - -template<typename Ret, typename... Args> -struct Wise<Ret (*) (Args...)> : public impl::Wise<Ret (Args...)> { }; - -template<typename Ret, typename Cls, typename... Args> -struct Wise<Ret (Cls::*)(Args...)> : public impl::Wise<Ret (Args...)> { }; - -template<typename Ret, typename Cls, typename... Args> -struct Wise<Ret (Cls::*)(Args...) const> : public impl::Wise<Ret (Args...)> { }; - -} // namespace Other - -template<typename T, typename E, typename Func, - typename Ret = - Rust::Result< - typename details::ResultOkType< - typename details::result_of<Func>::type - >::type, - E> - > -Ret map(const Rust::Result<T, E>& result, Func func) { - return ok::Map<Func>::map(result, func); -} - -template<typename T, typename E, typename Func, - typename Ret = - Rust::Result<T, - typename details::ResultErrType< - typename details::result_of<Func>::type - >::type - > - > -Ret mapError(const Rust::Result<T, E>& result, Func func) { - return err::Map<Func>::map(result, func); -} - -template<typename T, typename E, typename Func> -Rust::Result<T, E> then(const Rust::Result<T, E>& result, Func func) { - return And::Then<Func>::then(result, func); -} - -template<typename T, typename E, typename Func> -Rust::Result<T, E> otherwise(const Rust::Result<T, E>& result, Func func) { - return Other::Wise<Func>::otherwise(result, func); -} - -template<typename T, typename E, typename Func, - typename Ret = - Rust::Result<T, - typename details::ResultErrType< - typename details::result_of<Func>::type - >::type - > -> -Ret orElse(const Rust::Result<T, E>& result, Func func) { - return Or::Else<Func>::orElse(result, func); -} - -struct ok_tag { }; -struct err_tag { }; - -template<typename T, typename E> -struct Storage { - static constexpr size_t Size = sizeof(T) > sizeof(E) ? sizeof(T) : sizeof(E); - static constexpr size_t Align = sizeof(T) > sizeof(E) ? alignof(T) : alignof(E); - - typedef typename std::aligned_storage<Size, Align>::type type; - - Storage() - : initialized_(false) - { } - - void construct(types::Ok<T> ok) - { - new (&storage_) T(ok.val); - initialized_ = true; - } - void construct(types::Err<E> err) - { - new (&storage_) E(err.val); - initialized_ = true; - } - - template<typename U> - void rawConstruct(U&& val) { - typedef typename std::decay<U>::type CleanU; - - new (&storage_) CleanU(std::forward<U>(val)); - initialized_ = true; - } - - template<typename U> - const U& get() const { - return *reinterpret_cast<const U *>(&storage_); - } - - template<typename U> - U& get() { - return *reinterpret_cast<U *>(&storage_); - } - - void destroy(ok_tag) { - if (initialized_) { - get<T>().~T(); - initialized_ = false; - } - } - - void destroy(err_tag) { - if (initialized_) { - get<E>().~E(); - initialized_ = false; - } - } - - type storage_; - bool initialized_; -}; - -template<typename E> -struct Storage<void, E> { - typedef typename std::aligned_storage<sizeof(E), alignof(E)>::type type; - - void construct(types::Ok<void>) - { - initialized_ = true; - } - - void construct(types::Err<E> err) - { - new (&storage_) E(err.val); - initialized_ = true; - } - - template<typename U> - void rawConstruct(U&& val) { - typedef typename std::decay<U>::type CleanU; - - new (&storage_) CleanU(std::forward<U>(val)); - initialized_ = true; - } - - void destroy(ok_tag) { initialized_ = false; } - void destroy(err_tag) { - if (initialized_) { - get<E>().~E(); initialized_ = false; - } - } - - template<typename U> - const U& get() const { - return *reinterpret_cast<const U *>(&storage_); - } - - template<typename U> - U& get() { - return *reinterpret_cast<U *>(&storage_); - } - - type storage_; - bool initialized_; -}; - -template<typename T, typename E> -struct Constructor { - - static void move(Storage<T, E>&& src, Storage<T, E>& dst, ok_tag) { - dst.rawConstruct(std::move(src.template get<T>())); - src.destroy(ok_tag()); - } - - static void copy(const Storage<T, E>& src, Storage<T, E>& dst, ok_tag) { - dst.rawConstruct(src.template get<T>()); - } - - static void move(Storage<T, E>&& src, Storage<T, E>& dst, err_tag) { - dst.rawConstruct(std::move(src.template get<E>())); - src.destroy(err_tag()); - } - - static void copy(const Storage<T, E>& src, Storage<T, E>& dst, err_tag) { - dst.rawConstruct(src.template get<E>()); - } -}; - -template<typename E> -struct Constructor<void, E> { - static void move(Storage<void, E>&& src, Storage<void, E>& dst, ok_tag) { - } - - static void copy(const Storage<void, E>& src, Storage<void, E>& dst, ok_tag) { - } - - static void move(Storage<void, E>&& src, Storage<void, E>& dst, err_tag) { - dst.rawConstruct(std::move(src.template get<E>())); - src.destroy(err_tag()); - } - - static void copy(const Storage<void, E>& src, Storage<void, E>& dst, err_tag) { - dst.rawConstruct(src.template get<E>()); - } -}; - -} // namespace details - -namespace rpog { - -template<typename T, typename = void> struct EqualityComparable : std::false_type { }; - -template<typename T> -struct EqualityComparable<T, -typename std::enable_if< - true, - typename details::void_t<decltype(std::declval<T>() == std::declval<T>())>::type - >::type -> : std::true_type -{ -}; - - -} // namespace rpog - -namespace Rust { -template<typename T, typename E> -struct Result { - - static_assert(!std::is_same<E, void>::value, "void error type is not allowed"); - - typedef details::Storage<T, E> storage_type; - - Result(types::Ok<T> ok) - : ok_(true) - { - storage_.construct(std::move(ok)); - } - - Result(types::Err<E> err) - : ok_(false) - { - storage_.construct(std::move(err)); - } - - Result(Result&& other) { - if (other.isOk()) { - details::Constructor<T, E>::move(std::move(other.storage_), storage_, details::ok_tag()); - ok_ = true; - } else { - details::Constructor<T, E>::move(std::move(other.storage_), storage_, details::err_tag()); - ok_ = false; - } - } - - Result(const Result& other) { - if (other.isOk()) { - details::Constructor<T, E>::copy(other.storage_, storage_, details::ok_tag()); - ok_ = true; - } else { - details::Constructor<T, E>::copy(other.storage_, storage_, details::err_tag()); - ok_ = false; - } - } - - ~Result() { - if (ok_) - storage_.destroy(details::ok_tag()); - else - storage_.destroy(details::err_tag()); - } - - bool isOk() const { - return ok_; - } - - bool isErr() const { - return !ok_; - } - - T expect(const char* str) const { - if (!isOk()) { - std::fprintf(stderr, "%s\n", str); - std::terminate(); - } - return expect_impl(std::is_same<T, void>()); - } - - template<typename Func, - typename Ret = - Result< - typename details::ResultOkType< - typename details::result_of<Func>::type - >::type, - E> - > - Ret map(Func func) const { - return details::map(*this, func); - } - - template<typename Func, - typename Ret = - Result<T, - typename details::ResultErrType< - typename details::result_of<Func>::type - >::type - > - > - Ret mapError(Func func) const { - return details::mapError(*this, func); - } - - template<typename Func> - Result<T, E> then(Func func) const { - return details::then(*this, func); - } - - template<typename Func> - Result<T, E> otherwise(Func func) const { - return details::otherwise(*this, func); - } - - template<typename Func, - typename Ret = - Result<T, - typename details::ResultErrType< - typename details::result_of<Func>::type - >::type - > - > - Ret orElse(Func func) const { - return details::orElse(*this, func); - } - - storage_type& storage() { - return storage_; - } - - const storage_type& storage() const { - return storage_; - } - - template<typename U = T> - typename std::enable_if< - !std::is_same<U, void>::value, - U - >::type - unwrapOr(const U& defaultValue) const { - if (isOk()) { - return storage().template get<U>(); - } - return defaultValue; - } - - template<typename U = T> - typename std::enable_if< - !std::is_same<U, void>::value, - U - >::type - unwrap() const { - if (isOk()) { - return storage().template get<U>(); - } - - std::fprintf(stderr, "Attempting to unwrap an error Result\n"); - std::terminate(); - } - - E unwrapErr() const { - if (isErr()) { - return storage().template get<E>(); - } - - std::fprintf(stderr, "Attempting to unwrapErr an ok Result\n"); - std::terminate(); - } - -private: - T expect_impl(std::true_type) const { } - T expect_impl(std::false_type) const { return storage_.template get<T>(); } - - bool ok_; - storage_type storage_; -}; - -template<typename T, typename E> -bool operator==(const Rust::Result<T, E>& lhs, const Rust::Result<T, E>& rhs) { - static_assert(rpog::EqualityComparable<T>::value, "T must be EqualityComparable for Result to be comparable"); - static_assert(rpog::EqualityComparable<E>::value, "E must be EqualityComparable for Result to be comparable"); - - if (lhs.isOk() && rhs.isOk()) { - return lhs.storage().template get<T>() == rhs.storage().template get<T>(); - } - if (lhs.isErr() && rhs.isErr()) { - return lhs.storage().template get<E>() == rhs.storage().template get<E>(); - } -} - -template<typename T, typename E> -bool operator==(const Rust::Result<T, E>& lhs, types::Ok<T> ok) { - static_assert(rpog::EqualityComparable<T>::value, "T must be EqualityComparable for Result to be comparable"); - - if (!lhs.isOk()) return false; - - return lhs.storage().template get<T>() == ok.val; -} - -template<typename E> -bool operator==(const Rust::Result<void, E>& lhs, types::Ok<void>) { - return lhs.isOk(); -} - -template<typename T, typename E> -bool operator==(const Rust::Result<T, E>& lhs, types::Err<E> err) { - static_assert(rpog::EqualityComparable<E>::value, "E must be EqualityComparable for Result to be comparable"); - if (!lhs.isErr()) return false; - - return lhs.storage().template get<E>() == err.val; -} -} // end namespace Rust - -#define TRY(...) \ - ({ \ - auto res = __VA_ARGS__; \ - if (!res.isOk()) { \ - typedef details::ResultErrType<decltype(res)>::type E; \ - return types::Err<E>(res.storage().get<E>()); \ - } \ - typedef details::ResultOkType<decltype(res)>::type T; \ - res.storage().get<T>(); \ - })