#pragma once #include "error.h" #include "logger.h" #include "panic.h" #include #include #include struct DUMMY_VALUE {}; template class [[nodiscard]] result { struct ErrorContainer { std::variant error; }; struct SuccessContainer { Result success; }; std::variant value; public: result(Result success) : value(SuccessContainer { success }) {} result(std::variant error) : value(ErrorContainer { error }) {} // Expects the result to be successful, otherwise panic with error message Result expect(std::string errMsg = "Unwrapped a result with failure value") { if (is_success()) return std::get(value).success; Logger::fatalError(errMsg); panic(); } bool is_success() { return std::holds_alternative(value); } bool is_error() { return std::holds_alternative(value); } std::optional success() { return is_success() ? std::get(value).success : std::nullopt; } std::optional> error() { return is_error() ? std::make_optional(std::get(value).error) : std::nullopt; } void logError(Logger::LogLevel logLevel = Logger::LogLevel::ERROR) { if (is_success()) return; std::visit([&](auto&& it) { it.logMessage(logLevel); }, error().value()); } // Equivalent to .success operator std::optional() { return success(); } operator bool() { return is_success(); } bool operator !() { return is_error(); } }; template class fallible : public result { public: fallible() : result(DUMMY_VALUE {}) {} fallible(std::variant error) : result(error) {} };