refactor(error): added guards to result and misc refactorings to error
This commit is contained in:
parent
ff1f9be5c5
commit
58b53a0f02
8 changed files with 63 additions and 67 deletions
|
@ -1,17 +0,0 @@
|
|||
#include "error.h"
|
||||
#include "logger.h"
|
||||
|
||||
Error::Error(std::string message) : message(message) {
|
||||
}
|
||||
|
||||
std::string Error::getMessage() {
|
||||
return this->message;
|
||||
}
|
||||
|
||||
void Error::logMessage(Logger::LogLevel logLevel) {
|
||||
Logger::log(this->message, logLevel);
|
||||
}
|
||||
|
||||
void Error::logMessageFatal() {
|
||||
Logger::fatalError(this->message);
|
||||
}
|
|
@ -5,13 +5,15 @@
|
|||
|
||||
// Base class for all errors
|
||||
class Error {
|
||||
std::string message;
|
||||
std::string _errorType;
|
||||
std::string _message;
|
||||
|
||||
protected:
|
||||
Error(std::string message);
|
||||
inline Error(std::string errorType, std::string message) : _errorType(errorType), _message(message) {}
|
||||
|
||||
public:
|
||||
std::string getMessage();
|
||||
void logMessage(Logger::LogLevel logLevel = Logger::LogLevel::ERROR);
|
||||
void logMessageFatal();
|
||||
inline std::string errorType() { return this->_errorType; }
|
||||
inline std::string message() { return this->_message; }
|
||||
inline void logMessage(Logger::LogLevel logLevel = Logger::LogLevel::ERROR) { Logger::log(this->_message, logLevel); }
|
||||
void logMessageFatal() { Logger::fatalError(this->_message); }
|
||||
};
|
18
core/src/error/instance.h
Normal file
18
core/src/error/instance.h
Normal file
|
@ -0,0 +1,18 @@
|
|||
#pragma once
|
||||
|
||||
#include "error.h"
|
||||
|
||||
class NoSuchInstance : public Error {
|
||||
public:
|
||||
inline NoSuchInstance(std::string className) : Error("NoSuchInstance", "Cannot create instance of unknown type " + className) {}
|
||||
};
|
||||
|
||||
class NoSuchService : public Error {
|
||||
public:
|
||||
inline NoSuchService(std::string className) : Error("NoSuchService", "Cannot insert service of unknown type " + className) {}
|
||||
};
|
||||
|
||||
class ServiceAlreadyExists : public Error {
|
||||
public:
|
||||
inline ServiceAlreadyExists(std::string className) : Error("ServiceAlreadyExists", "Service " + className + " is already inserted") {}
|
||||
};
|
|
@ -1,59 +1,68 @@
|
|||
#pragma once
|
||||
|
||||
#include "error.h"
|
||||
#include "error/error.h"
|
||||
#include "logger.h"
|
||||
#include "panic.h"
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
#include <variant>
|
||||
|
||||
struct DUMMY_VALUE {};
|
||||
|
||||
template <typename Result, typename ...E>
|
||||
template <typename T_Result, typename ...T_Errors>
|
||||
class [[nodiscard]] result {
|
||||
struct ErrorContainer {
|
||||
std::variant<E...> error;
|
||||
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 SuccessContainer {
|
||||
Result success;
|
||||
struct success_state {
|
||||
T_Result success;
|
||||
};
|
||||
|
||||
std::variant<SuccessContainer, ErrorContainer> value;
|
||||
std::variant<success_state, error_state> value;
|
||||
public:
|
||||
result(Result success) : value(SuccessContainer { success }) {}
|
||||
result(std::variant<E...> error) : value(ErrorContainer { error }) {}
|
||||
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
|
||||
Result expect(std::string errMsg = "Unwrapped a result with failure value") {
|
||||
if (is_success())
|
||||
return std::get<SuccessContainer>(value).success;
|
||||
Logger::fatalError(errMsg);
|
||||
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(), it.message(), errMsg);
|
||||
}, error().value());
|
||||
panic();
|
||||
}
|
||||
|
||||
bool is_success() { return std::holds_alternative<SuccessContainer>(value); }
|
||||
bool is_error() { return std::holds_alternative<ErrorContainer>(value); }
|
||||
bool isSuccess() { return std::holds_alternative<success_state>(value); }
|
||||
bool isError() { return std::holds_alternative<error_state>(value); }
|
||||
|
||||
std::optional<Result> success() { return is_success() ? std::get<SuccessContainer>(value).success : std::nullopt; }
|
||||
std::optional<std::variant<E...>> error() { return is_error() ? std::make_optional(std::get<ErrorContainer>(value).error) : std::nullopt; }
|
||||
std::optional<T_Result> success() { return isSuccess() ? 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 (is_success()) return;
|
||||
if (isSuccess()) return;
|
||||
std::visit([&](auto&& it) {
|
||||
it.logMessage(logLevel);
|
||||
}, error().value());
|
||||
}
|
||||
|
||||
// Equivalent to .success
|
||||
operator std::optional<Result>() { return success(); }
|
||||
operator bool() { return is_success(); }
|
||||
bool operator !() { return is_error(); }
|
||||
operator std::optional<T_Result>() { return success(); }
|
||||
operator bool() { return isSuccess(); }
|
||||
bool operator !() { return isError(); }
|
||||
};
|
||||
|
||||
template <typename ...E>
|
||||
class fallible : public result<DUMMY_VALUE, E...> {
|
||||
template <typename ...T_Errors>
|
||||
class [[nodiscard]] fallible : public result<DUMMY_VALUE, T_Errors...> {
|
||||
public:
|
||||
fallible() : result<DUMMY_VALUE, E...>(DUMMY_VALUE {}) {}
|
||||
fallible(std::variant<E...> error) : result<DUMMY_VALUE, E...>(error) {}
|
||||
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) {}
|
||||
};
|
|
@ -240,7 +240,7 @@ void Instance::Serialize(pugi::xml_node parent) {
|
|||
result<InstanceRef, NoSuchInstance> Instance::Deserialize(pugi::xml_node node) {
|
||||
std::string className = node.attribute("class").value();
|
||||
if (INSTANCE_MAP.count(className) == 0) {
|
||||
return std::variant<NoSuchInstance>(NoSuchInstance(className));
|
||||
return NoSuchInstance(className);
|
||||
}
|
||||
// This will error if an abstract instance is used in the file. Oh well, not my prob rn.
|
||||
// printf("What are you? A %s sandwich\n", className.c_str());
|
||||
|
@ -265,7 +265,7 @@ result<InstanceRef, NoSuchInstance> Instance::Deserialize(pugi::xml_node node) {
|
|||
// Read children
|
||||
for (pugi::xml_node childNode : node.children("Item")) {
|
||||
result<InstanceRef, NoSuchInstance> child = Instance::Deserialize(childNode);
|
||||
if (child.is_error()) {
|
||||
if (child.isError()) {
|
||||
std::get<NoSuchInstance>(child.error().value()).logMessage();
|
||||
continue;
|
||||
}
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
#include <expected.hpp>
|
||||
#include <pugixml.hpp>
|
||||
|
||||
#include "error/error.h"
|
||||
#include "error/instance.h"
|
||||
#include "error/result.h"
|
||||
#include "member.h"
|
||||
|
||||
|
@ -38,11 +38,6 @@ struct InstanceType {
|
|||
InstanceFlags flags;
|
||||
};
|
||||
|
||||
// Errors
|
||||
class NoSuchInstance : public Error {
|
||||
public:
|
||||
inline NoSuchInstance(std::string className) : Error("Cannot create instance of unknown type " + className) {}
|
||||
};
|
||||
|
||||
class DescendantsIterator;
|
||||
|
||||
|
|
|
@ -93,7 +93,7 @@ void DataModel::DeserializeService(pugi::xml_node node) {
|
|||
// Add children
|
||||
for (pugi::xml_node childNode : node.children("Item")) {
|
||||
result<InstanceRef, NoSuchInstance> child = Instance::Deserialize(childNode);
|
||||
if (child.is_error()) {
|
||||
if (child.isError()) {
|
||||
std::get<NoSuchInstance>(child.error().value()).logMessage();
|
||||
continue;
|
||||
}
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
#pragma once
|
||||
|
||||
#include "base.h"
|
||||
#include "error/result.h"
|
||||
#include "logger.h"
|
||||
#include "objects/base/instance.h"
|
||||
|
@ -14,16 +13,6 @@ class Workspace;
|
|||
class DataModel;
|
||||
class Service;
|
||||
|
||||
class NoSuchService : public Error {
|
||||
public:
|
||||
inline NoSuchService(std::string className) : Error("Cannot insert service of unknown type " + className) {}
|
||||
};
|
||||
|
||||
class ServiceAlreadyExists : public Error {
|
||||
public:
|
||||
inline ServiceAlreadyExists(std::string className) : Error("Service " + className + " is already inserted") {}
|
||||
};
|
||||
|
||||
// The root instance to all objects in the hierarchy
|
||||
class DataModel : public Instance {
|
||||
private:
|
||||
|
|
Loading…
Add table
Reference in a new issue