75 lines
No EOL
2.8 KiB
C++
75 lines
No EOL
2.8 KiB
C++
#pragma once
|
|
|
|
#include "error/error.h"
|
|
#include "logger.h"
|
|
#include "panic.h"
|
|
#include <optional>
|
|
#include <string>
|
|
#include <type_traits>
|
|
#include <variant>
|
|
|
|
struct DUMMY_VALUE {};
|
|
|
|
template <typename T_Result, typename ...T_Errors>
|
|
class [[nodiscard]] result {
|
|
static_assert(std::conjunction_v<std::is_base_of<Error, T_Errors>...>, "result<T_Errors...> requires T_Errors to derive from Error");
|
|
|
|
struct error_state {
|
|
std::variant<T_Errors...> error;
|
|
};
|
|
|
|
struct success_state {
|
|
T_Result success;
|
|
};
|
|
|
|
std::variant<success_state, error_state> value;
|
|
public:
|
|
result(T_Result success) : value(success_state { success }) {}
|
|
result(std::variant<T_Errors...> error) : value(error_state { error }) {}
|
|
template <typename T_Error, std::enable_if_t<std::disjunction_v<std::is_same<T_Error, T_Errors>...>, int> = 0>
|
|
result(T_Error error) : value(error_state { error }) {}
|
|
|
|
// Expects the result to be successful, otherwise panic with error message
|
|
T_Result expect(std::string errMsg = "Expected result to contain success") {
|
|
if (isSuccess())
|
|
return std::get<success_state>(value).success;
|
|
std::visit([&](auto&& it) {
|
|
Logger::fatalErrorf("Unwrapped a result with error value: [%s] %s\n\t%s", it.errorType().c_str(), it.message().c_str(), errMsg.c_str());
|
|
}, error().value());
|
|
panic();
|
|
}
|
|
|
|
bool isSuccess() { return std::holds_alternative<success_state>(value); }
|
|
bool isError() { return std::holds_alternative<error_state>(value); }
|
|
|
|
std::optional<T_Result> success() { return isSuccess() ? std::make_optional(std::get<success_state>(value).success) : std::nullopt; }
|
|
std::optional<std::variant<T_Errors...>> error() { return isError() ? std::make_optional(std::get<error_state>(value).error) : std::nullopt; }
|
|
|
|
void logError(Logger::LogLevel logLevel = Logger::LogLevel::ERROR) {
|
|
if (isSuccess()) return;
|
|
std::visit([&](auto&& it) {
|
|
it.logMessage(logLevel);
|
|
}, error().value());
|
|
}
|
|
|
|
std::optional<std::string> errorMessage() {
|
|
if (isSuccess()) return std::nullopt;
|
|
return std::visit([&](auto&& it) {
|
|
return it.message();
|
|
}, error().value());
|
|
}
|
|
|
|
// Equivalent to .success
|
|
operator std::optional<T_Result>() { return success(); }
|
|
operator bool() { return isSuccess(); }
|
|
bool operator !() { return isError(); }
|
|
};
|
|
|
|
template <typename ...T_Errors>
|
|
class [[nodiscard]] fallible : public result<DUMMY_VALUE, T_Errors...> {
|
|
public:
|
|
fallible() : result<DUMMY_VALUE, T_Errors...>(DUMMY_VALUE {}) {}
|
|
fallible(std::variant<T_Errors...> error) : result<DUMMY_VALUE, T_Errors...>(error) {}
|
|
template <typename T_Error, std::enable_if_t<std::disjunction_v<std::is_same<T_Error, T_Errors>...>, int> = 0>
|
|
fallible(T_Error error) : result<DUMMY_VALUE, T_Errors...>(error) {}
|
|
}; |