2024-06-07 04:23:11 -04:00
|
|
|
#pragma once
|
|
|
|
|
|
|
|
#include <stdexcept>
|
|
|
|
#include <string>
|
|
|
|
#include <utility>
|
|
|
|
#include <variant>
|
|
|
|
|
2024-06-16 00:13:15 -04:00
|
|
|
#include "util/macros.h"
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @class Error
|
|
|
|
* @brief Represents an error with a message.
|
|
|
|
*
|
|
|
|
* This class is used to encapsulate error messages that can be returned from functions.
|
|
|
|
*/
|
2024-06-07 04:23:11 -04:00
|
|
|
class Error {
|
|
|
|
public:
|
2024-06-16 00:13:15 -04:00
|
|
|
/**
|
|
|
|
* @brief Constructs an Error with a message.
|
|
|
|
* @param message The error message.
|
|
|
|
*/
|
2024-06-07 04:23:11 -04:00
|
|
|
explicit Error(std::string message) : m_Message(std::move(message)) {}
|
2024-06-16 00:13:15 -04:00
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Retrieves the error message.
|
|
|
|
* @return A constant reference to the error message string.
|
|
|
|
*/
|
2024-06-09 20:14:51 -04:00
|
|
|
[[nodiscard]] fn message() const -> const std::string& { return m_Message; }
|
2024-06-07 04:23:11 -04:00
|
|
|
|
|
|
|
private:
|
2024-06-16 00:13:15 -04:00
|
|
|
std::string m_Message; ///< The error message.
|
2024-06-07 04:23:11 -04:00
|
|
|
};
|
|
|
|
|
|
|
|
// Primary template for Result with a default type of void
|
2024-06-16 00:13:15 -04:00
|
|
|
|
|
|
|
/**
|
|
|
|
* @class Result
|
|
|
|
* @brief Represents a result that can either be a value or an error.
|
|
|
|
*
|
|
|
|
* This is the primary template for Result, which defaults to handling void results.
|
|
|
|
*/
|
2024-06-07 04:23:11 -04:00
|
|
|
template <typename T = void>
|
|
|
|
class Result;
|
|
|
|
|
|
|
|
// Specialization for Result<void>
|
2024-06-16 00:13:15 -04:00
|
|
|
|
|
|
|
/**
|
|
|
|
* @class Result<void>
|
|
|
|
* @brief Specialization of Result for handling void results.
|
|
|
|
*
|
|
|
|
* This class is used when a function either succeeds with no value or fails with an error.
|
|
|
|
*/
|
2024-06-07 04:23:11 -04:00
|
|
|
template <>
|
|
|
|
class Result<void> {
|
|
|
|
public:
|
2024-06-16 00:13:15 -04:00
|
|
|
/**
|
|
|
|
* @brief Constructs a successful Result.
|
|
|
|
*/
|
2024-06-07 04:23:11 -04:00
|
|
|
Result() : m_Result(std::monostate {}) {}
|
2024-06-16 00:13:15 -04:00
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Constructs an error Result.
|
|
|
|
* @param error The error object.
|
|
|
|
*/
|
2024-06-07 04:23:11 -04:00
|
|
|
Result(const Error& error) : m_Result(error) {}
|
|
|
|
|
2024-06-16 00:13:15 -04:00
|
|
|
/**
|
|
|
|
* @brief Constructs an error Result.
|
|
|
|
* @param error An rvalue reference to the error object.
|
|
|
|
*/
|
|
|
|
Result(Error&& error) : m_Result(std::move(error)) {}
|
2024-06-07 04:23:11 -04:00
|
|
|
|
2024-06-16 00:13:15 -04:00
|
|
|
/**
|
|
|
|
* @brief Checks if the Result is successful.
|
|
|
|
* @return True if the Result is successful, otherwise false.
|
|
|
|
*/
|
|
|
|
[[nodiscard]] fn isOk() const -> bool { return std::holds_alternative<std::monostate>(m_Result); }
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Checks if the Result contains an error.
|
|
|
|
* @return True if the Result contains an error, otherwise false.
|
|
|
|
*/
|
|
|
|
[[nodiscard]] fn isErr() const -> bool { return std::holds_alternative<Error>(m_Result); }
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Throws an exception if the Result contains an error.
|
|
|
|
*
|
|
|
|
* This function should be called only if the Result is successful.
|
|
|
|
*/
|
2024-06-07 04:23:11 -04:00
|
|
|
void value() const {
|
|
|
|
if (isErr()) {
|
|
|
|
throw std::logic_error("Attempted to access value of an error Result");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-06-16 00:13:15 -04:00
|
|
|
/**
|
|
|
|
* @brief Retrieves the error object.
|
|
|
|
* @return A constant reference to the Error object.
|
|
|
|
* @throws std::logic_error if the Result is successful.
|
|
|
|
*/
|
2024-06-09 20:14:51 -04:00
|
|
|
[[nodiscard]] fn error() const -> const Error& {
|
2024-06-07 04:23:11 -04:00
|
|
|
if (isOk()) {
|
|
|
|
throw std::logic_error("Attempted to access error of an ok Result");
|
|
|
|
}
|
|
|
|
return std::get<Error>(m_Result);
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
2024-06-16 00:13:15 -04:00
|
|
|
std::variant<std::monostate, Error>
|
|
|
|
m_Result; ///< The underlying result, which can be either void or an Error.
|
2024-06-07 04:23:11 -04:00
|
|
|
};
|
|
|
|
|
|
|
|
// Primary template for Result
|
2024-06-16 00:13:15 -04:00
|
|
|
|
|
|
|
/**
|
|
|
|
* @class Result
|
|
|
|
* @brief Represents a result that can either be a value of type T or an error.
|
|
|
|
*
|
|
|
|
* This template class is used to handle results that can either be a successful value or an error.
|
|
|
|
* @tparam T The type of the successful value.
|
|
|
|
*/
|
2024-06-07 04:23:11 -04:00
|
|
|
template <typename T>
|
|
|
|
class Result {
|
|
|
|
public:
|
2024-06-16 00:13:15 -04:00
|
|
|
/**
|
|
|
|
* @brief Constructs a successful Result with a value.
|
|
|
|
* @param value The value of the Result.
|
|
|
|
*/
|
2024-06-07 04:23:11 -04:00
|
|
|
Result(const T& value) : m_Result(value) {}
|
2024-06-16 00:13:15 -04:00
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Constructs a successful Result with a value.
|
|
|
|
* @param value An rvalue reference to the value.
|
|
|
|
*/
|
2024-06-07 04:23:11 -04:00
|
|
|
Result(T&& value) : m_Result(std::move(value)) {}
|
2024-06-16 00:13:15 -04:00
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Constructs an error Result.
|
|
|
|
* @param error The error object.
|
|
|
|
*/
|
2024-06-07 04:23:11 -04:00
|
|
|
Result(const Error& error) : m_Result(error) {}
|
|
|
|
|
2024-06-16 00:13:15 -04:00
|
|
|
/**
|
|
|
|
* @brief Constructs an error Result.
|
|
|
|
* @param error An rvalue reference to the error object.
|
|
|
|
*/
|
|
|
|
Result(Error&& error) : m_Result(std::move(error)) {}
|
2024-06-07 04:23:11 -04:00
|
|
|
|
2024-06-16 00:13:15 -04:00
|
|
|
/**
|
|
|
|
* @brief Checks if the Result is successful.
|
|
|
|
* @return True if the Result is successful, otherwise false.
|
|
|
|
*/
|
|
|
|
[[nodiscard]] fn isOk() const -> bool { return std::holds_alternative<T>(m_Result); }
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Checks if the Result contains an error.
|
|
|
|
* @return True if the Result contains an error, otherwise false.
|
|
|
|
*/
|
|
|
|
[[nodiscard]] fn isErr() const -> bool { return std::holds_alternative<Error>(m_Result); }
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Retrieves the value.
|
|
|
|
* @return A constant reference to the value.
|
|
|
|
* @throws std::logic_error if the Result contains an error.
|
|
|
|
*/
|
2024-06-09 20:14:51 -04:00
|
|
|
fn value() const -> const T& {
|
2024-06-07 04:23:11 -04:00
|
|
|
if (isErr()) {
|
|
|
|
throw std::logic_error("Attempted to access value of an error Result");
|
|
|
|
}
|
|
|
|
return std::get<T>(m_Result);
|
|
|
|
}
|
|
|
|
|
2024-06-16 00:13:15 -04:00
|
|
|
/**
|
|
|
|
* @brief Retrieves the error object.
|
|
|
|
* @return A constant reference to the Error object.
|
|
|
|
* @throws std::logic_error if the Result is successful.
|
|
|
|
*/
|
2024-06-09 20:14:51 -04:00
|
|
|
[[nodiscard]] fn error() const -> const Error& {
|
2024-06-07 04:23:11 -04:00
|
|
|
if (isOk()) {
|
|
|
|
throw std::logic_error("Attempted to access error of an ok Result");
|
|
|
|
}
|
|
|
|
return std::get<Error>(m_Result);
|
|
|
|
}
|
|
|
|
|
2024-06-16 00:13:15 -04:00
|
|
|
/**
|
|
|
|
* @brief Retrieves the value or returns a default value.
|
|
|
|
* @param defaultValue The default value to return if the Result contains an error.
|
|
|
|
* @return The value if the Result is successful, otherwise the default value.
|
|
|
|
*/
|
2024-06-09 20:14:51 -04:00
|
|
|
fn valueOr(const T& defaultValue) const -> T {
|
2024-06-07 04:23:11 -04:00
|
|
|
return isOk() ? std::get<T>(m_Result) : defaultValue;
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
2024-06-16 00:13:15 -04:00
|
|
|
std::variant<T, Error>
|
|
|
|
m_Result; ///< The underlying result, which can be either a value of type T or an Error.
|
2024-06-07 04:23:11 -04:00
|
|
|
};
|
|
|
|
|
2024-06-16 00:13:15 -04:00
|
|
|
/**
|
|
|
|
* @brief Helper function to create a successful Result.
|
|
|
|
*
|
|
|
|
* This function deduces the type of the value and creates a successful Result.
|
|
|
|
* @tparam T The type of the value.
|
|
|
|
* @param value The value to be stored in the Result.
|
|
|
|
* @return A Result object containing the value.
|
|
|
|
*/
|
2024-06-07 04:23:11 -04:00
|
|
|
template <typename T>
|
2024-06-09 20:14:51 -04:00
|
|
|
fn Ok(T&& value) {
|
2024-06-07 04:23:11 -04:00
|
|
|
return Result<std::decay_t<T>>(std::forward<T>(value));
|
|
|
|
}
|
|
|
|
|
2024-06-16 00:13:15 -04:00
|
|
|
/**
|
|
|
|
* @brief Helper function to create a successful Result<void>.
|
|
|
|
*
|
|
|
|
* This function creates a successful Result that does not contain a value.
|
|
|
|
* @return A Result<void> object indicating success.
|
|
|
|
*/
|
2024-06-09 20:14:51 -04:00
|
|
|
inline fn Ok() -> Result<void> { return {}; }
|