347 lines
6.6 KiB
C++
347 lines
6.6 KiB
C++
/*
|
|
* Copyright 2015 - 2025 gary@drinkingtea.net
|
|
*
|
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
|
*/
|
|
|
|
#pragma once
|
|
|
|
#if __has_include(<exception>)
|
|
#include <exception>
|
|
#else
|
|
namespace std {
|
|
class exception {
|
|
public:
|
|
virtual ~exception() = default;
|
|
|
|
[[nodiscard]]
|
|
virtual char const*what() const noexcept {
|
|
return "";
|
|
}
|
|
};
|
|
}
|
|
#endif
|
|
|
|
#include "defines.hpp"
|
|
#include "def.hpp"
|
|
#include "source_location.hpp"
|
|
#include "typetraits.hpp"
|
|
#include "utility.hpp"
|
|
|
|
namespace ox {
|
|
|
|
using ErrorCode = uint16_t;
|
|
|
|
|
|
struct [[nodiscard]] Error {
|
|
std::source_location src;
|
|
ox::CString msg = nullptr;
|
|
ErrorCode errCode = 0;
|
|
|
|
constexpr Error() noexcept = default;
|
|
|
|
explicit constexpr Error(
|
|
ErrorCode const errCode,
|
|
std::source_location const&src = std::source_location::current()) noexcept:
|
|
src{src},
|
|
errCode{errCode}
|
|
{}
|
|
|
|
explicit constexpr Error(
|
|
ErrorCode const errCode,
|
|
ox::CString msg,
|
|
std::source_location const&src = std::source_location::current()) noexcept:
|
|
src{src},
|
|
msg{msg},
|
|
errCode{errCode}
|
|
{}
|
|
|
|
constexpr operator uint64_t() const noexcept {
|
|
return errCode;
|
|
}
|
|
|
|
};
|
|
|
|
[[nodiscard]]
|
|
constexpr auto errCode(Error const&err) noexcept {
|
|
return err.errCode;
|
|
}
|
|
|
|
template<typename T = char const*>
|
|
[[nodiscard]]
|
|
constexpr auto toStr(Error const&err) noexcept {
|
|
return err.msg ? T{err.msg} : "";
|
|
}
|
|
|
|
struct Exception: public std::exception {
|
|
std::source_location src;
|
|
ox::CString msg = nullptr;
|
|
ErrorCode errCode = 0;
|
|
|
|
explicit Exception(
|
|
ErrorCode const errCode,
|
|
std::source_location const&src = std::source_location::current()) noexcept:
|
|
src{src},
|
|
errCode{errCode} {}
|
|
|
|
explicit Exception(
|
|
ErrorCode const errCode,
|
|
ox::CString msg,
|
|
std::source_location const&src = std::source_location::current()) noexcept:
|
|
src{src},
|
|
msg{msg},
|
|
errCode{errCode} {}
|
|
|
|
explicit Exception(Error const&err) noexcept:
|
|
src{err.src},
|
|
msg{err.msg ? err.msg : ""},
|
|
errCode{err.errCode} {}
|
|
|
|
constexpr Error toError() const noexcept {
|
|
return Error(errCode, msg, src);
|
|
}
|
|
|
|
[[nodiscard]]
|
|
char const*what() const noexcept override {
|
|
return msg;
|
|
}
|
|
};
|
|
|
|
[[noreturn]]
|
|
void panic(char const*file, int line, char const*panicMsg, Error const&err) noexcept;
|
|
|
|
template<typename T>
|
|
struct [[nodiscard]] Result {
|
|
|
|
using type = typename remove_reference<T>::type;
|
|
|
|
T value;
|
|
Error error;
|
|
|
|
constexpr Result() noexcept: value(), error(0) {
|
|
}
|
|
|
|
template<typename U>
|
|
constexpr Result(Result<U> const&other) noexcept: value(other.value), error(other.error) {
|
|
}
|
|
|
|
template<typename U>
|
|
constexpr Result(Result<U> &&other) noexcept: value(std::move(other.value)), error(std::move(other.error)) {
|
|
}
|
|
|
|
constexpr Result(Error const&error) noexcept: value(), error(error) {
|
|
}
|
|
|
|
constexpr Result(type const&value, Error const&error = {}) noexcept: value(value), error(error) {
|
|
}
|
|
|
|
constexpr Result(type &&value, Error const&error = {}) noexcept: value(std::move(value)), error(error) {
|
|
}
|
|
|
|
constexpr ~Result() noexcept = default;
|
|
|
|
explicit constexpr operator type const&() const noexcept {
|
|
return value;
|
|
}
|
|
|
|
explicit constexpr operator type&() noexcept {
|
|
return value;
|
|
}
|
|
|
|
[[nodiscard]]
|
|
constexpr bool ok() const noexcept {
|
|
return error == 0;
|
|
}
|
|
|
|
template<typename U>
|
|
constexpr Error copyTo(U &val) const& noexcept {
|
|
if (!error) [[likely]] {
|
|
val = value;
|
|
}
|
|
return error;
|
|
}
|
|
|
|
template<typename U>
|
|
constexpr Error copyTo(U &val) && noexcept {
|
|
if (!error) [[likely]] {
|
|
val = std::move(value);
|
|
}
|
|
return error;
|
|
}
|
|
|
|
template<typename U>
|
|
constexpr Error moveTo(U &val) noexcept {
|
|
if (!error) [[likely]] {
|
|
val = std::move(value);
|
|
}
|
|
return error;
|
|
}
|
|
|
|
[[nodiscard]]
|
|
constexpr T &unwrap() & noexcept {
|
|
if (error) {
|
|
oxPanic(error, "Failed unwrap");
|
|
}
|
|
return value;
|
|
}
|
|
|
|
[[nodiscard]]
|
|
constexpr T &&unwrap() && noexcept {
|
|
if (error) {
|
|
oxPanic(error, "Failed unwrap");
|
|
}
|
|
return std::move(value);
|
|
}
|
|
|
|
[[nodiscard]]
|
|
constexpr T const&unwrap() const & noexcept {
|
|
if (error) [[unlikely]] {
|
|
oxPanic(error, "Failed unwrap");
|
|
}
|
|
return value;
|
|
}
|
|
|
|
[[nodiscard]]
|
|
constexpr T &unwrapThrow() & {
|
|
if (error) {
|
|
throw ox::Exception(error);
|
|
}
|
|
return value;
|
|
}
|
|
|
|
[[nodiscard]]
|
|
constexpr T &&unwrapThrow() && {
|
|
if (error) {
|
|
throw ox::Exception(error);
|
|
}
|
|
return std::move(value);
|
|
}
|
|
|
|
[[nodiscard]]
|
|
constexpr T const&unwrapThrow() const & {
|
|
if (error) {
|
|
throw ox::Exception(error);
|
|
}
|
|
return value;
|
|
}
|
|
|
|
template<typename U = T>
|
|
constexpr ox::Result<U> to() & noexcept {
|
|
if (error) [[unlikely]] {
|
|
return error;
|
|
}
|
|
return U(value);
|
|
}
|
|
|
|
template<typename U = T>
|
|
constexpr ox::Result<U> to() && noexcept {
|
|
if (error) [[unlikely]] {
|
|
return error;
|
|
}
|
|
return U(std::move(value));
|
|
}
|
|
|
|
template<typename U = T>
|
|
constexpr ox::Result<U> to(auto const&f) & noexcept {
|
|
if (error) [[unlikely]] {
|
|
return error;
|
|
}
|
|
return f(value);
|
|
}
|
|
|
|
template<typename U = T>
|
|
constexpr ox::Result<U> to(auto const&f) && noexcept {
|
|
if (error) [[unlikely]] {
|
|
return error;
|
|
}
|
|
return f(std::move(value));
|
|
}
|
|
|
|
/**
|
|
* Returns parameter alt if Result contains an error.
|
|
* @param alt
|
|
* @return value of Result or alt
|
|
*/
|
|
constexpr T or_value(T &&alt) const& noexcept {
|
|
if (error) {
|
|
return std::move(alt);
|
|
}
|
|
return value;
|
|
}
|
|
|
|
/**
|
|
* Returns parameter alt if Result contains an error.
|
|
* @param alt
|
|
* @return value of Result or alt
|
|
*/
|
|
constexpr T or_value(T &&alt) && noexcept {
|
|
if (error) {
|
|
return std::move(alt);
|
|
}
|
|
return std::move(value);
|
|
}
|
|
|
|
/**
|
|
* Returns parameter alt if Result contains an error.
|
|
* @param alt
|
|
* @return value of Result or alt
|
|
*/
|
|
constexpr T or_value(T const&alt) const& noexcept {
|
|
if (error) {
|
|
return alt;
|
|
}
|
|
return value;
|
|
}
|
|
|
|
/**
|
|
* Returns parameter alt if Result contains an error.
|
|
* @param alt
|
|
* @return value of Result or alt
|
|
*/
|
|
constexpr T or_value(T const&alt) && noexcept {
|
|
if (error) {
|
|
return alt;
|
|
}
|
|
return std::move(value);
|
|
}
|
|
|
|
};
|
|
|
|
namespace detail {
|
|
|
|
constexpr Error toError(Error const&e) noexcept {
|
|
return e;
|
|
}
|
|
|
|
template<typename T>
|
|
constexpr Error toError(Result<T> const&r) noexcept {
|
|
return r.error;
|
|
}
|
|
|
|
}
|
|
|
|
constexpr void primitiveAssert(char const*file, int line, bool pass, char const*msg) noexcept {
|
|
if constexpr(ox::defines::Debug) {
|
|
if (!pass) [[unlikely]] {
|
|
panic(file, line, msg, ox::Error(1));
|
|
}
|
|
}
|
|
}
|
|
|
|
constexpr void boundsCheck(
|
|
char const*file,
|
|
int const line,
|
|
size_t const i,
|
|
size_t const sz,
|
|
char const*msg) noexcept {
|
|
if constexpr(defines::CheckBounds) {
|
|
if (i >= sz) [[unlikely]] {
|
|
panic(file, line, msg, ox::Error{1});
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|