199 lines
6.3 KiB
C++
199 lines
6.3 KiB
C++
// Copyright (c) 2020-now by the Zeek Project. See LICENSE for details.
|
|
|
|
#pragma once
|
|
|
|
#include <optional>
|
|
#include <ostream>
|
|
#include <string>
|
|
#include <utility>
|
|
#include <variant>
|
|
|
|
#include <hilti/rt/exception.h>
|
|
|
|
namespace hilti::rt {
|
|
|
|
namespace result {
|
|
|
|
/** Represents an error message. */
|
|
class Error {
|
|
public:
|
|
Error(std::string description = "<no description>", std::string context = "")
|
|
: _description(std::move(description)), _context(std::move(context)) {}
|
|
const auto& description() const { return _description; }
|
|
const auto& context() const { return _context; }
|
|
operator std::string() const { return _description; }
|
|
operator std::string_view() const { return _description; }
|
|
|
|
private:
|
|
std::string _description;
|
|
std::string _context;
|
|
};
|
|
|
|
inline std::ostream& operator<<(std::ostream& out, const Error& error) {
|
|
out << error.description();
|
|
return out;
|
|
}
|
|
|
|
inline bool operator==(const Error& a, const Error& b) { return a.description() == b.description(); }
|
|
inline bool operator!=(const Error& a, const Error& b) { return ! (a == b); }
|
|
|
|
/** Exception indicating that no result is available if though one was requested. */
|
|
class NoResult : public RuntimeError {
|
|
public:
|
|
NoResult(Error err) : RuntimeError(err.description()), _error(std::move(err)) {}
|
|
|
|
private:
|
|
Error _error;
|
|
};
|
|
|
|
/** Exception indicating that no error has been reported if though one was expected to be available. */
|
|
class NoError : public RuntimeError {
|
|
public:
|
|
NoError() : RuntimeError("<no error>") {}
|
|
};
|
|
|
|
} // namespace result
|
|
|
|
struct Nothing {};
|
|
|
|
inline bool operator==(const Nothing&, const Nothing&) { return true; }
|
|
inline bool operator!=(const Nothing&, const Nothing&) { return false; }
|
|
|
|
namespace detail::adl {
|
|
inline std::string to_string(const Nothing& n, adl::tag /*unused*/) { return "<nothing>"; };
|
|
} // namespace detail::adl
|
|
|
|
/**
|
|
* Represents either a successful result from function if it returned one, or
|
|
* reflects an error if the function was unsuccessful.
|
|
*/
|
|
template<typename T>
|
|
class Result {
|
|
public:
|
|
Result() : _value(std::in_place_type_t<result::Error>(), result::Error("<result not initialized>")) {}
|
|
|
|
/** Creates a successful result from a value. */
|
|
Result(const T& t) : _value(std::in_place_type_t<T>(), t) {}
|
|
/** Creates a successful result from a value. */
|
|
Result(T&& t) : _value(std::in_place_type_t<T>(), std::move(t)) {}
|
|
/** Creates an result reflecting an error. */
|
|
Result(const result::Error& e) : _value(std::in_place_type_t<result::Error>(), e) {}
|
|
/** Creates an result reflecting an error. */
|
|
Result(result::Error&& e) : _value(std::in_place_type_t<result::Error>(), std::move(e)) {}
|
|
|
|
Result(const Result& o) = default;
|
|
Result(Result&& o) = default; // NOLINT (hicpp-noexcept-move)
|
|
~Result() = default;
|
|
|
|
/**
|
|
* Returns the result's value, assuming it indicates success.
|
|
*
|
|
* @exception `std::bad_variant_access` if the result reflects an error
|
|
* state
|
|
*/
|
|
const T& value() const { return std::get<T>(_value); }
|
|
|
|
/**
|
|
* Returns the result's value, assuming it indicates success.
|
|
*
|
|
* @exception `std::bad_variant_access` if the result reflects an error
|
|
* state
|
|
*/
|
|
T& value() { return std::get<T>(_value); }
|
|
|
|
/**
|
|
* Returns the result's value if it indicates success, or throws an
|
|
* exception if not. By default, the exception thrown is `result::NoResult`.
|
|
*
|
|
* @tparam E type of the exception to throw
|
|
* @exception exception of type `E` if the result reflects an error state
|
|
*/
|
|
template<typename E = result::NoResult>
|
|
const T& valueOrThrow() const {
|
|
if ( ! hasValue() )
|
|
throw E(error());
|
|
|
|
return value();
|
|
}
|
|
|
|
/**
|
|
* Returns the result's value if it indicates success, or throws an
|
|
* exception if not.
|
|
*
|
|
* @tparam E type of the exception to throw
|
|
* @exception exception of type `E` if the result reflects an error state
|
|
*/
|
|
template<typename E = result::NoResult>
|
|
T& valueOrThrow() {
|
|
if ( ! hasValue() )
|
|
throw E(error());
|
|
|
|
return value();
|
|
}
|
|
|
|
/**
|
|
* Returns the result's error, assuming it reflect one.
|
|
*
|
|
* @exception `std::bad_variant_access` if the result doe not reflect an error
|
|
* state
|
|
*/
|
|
const result::Error& error() const { return std::get<result::Error>(_value); }
|
|
|
|
/**
|
|
* Returns the result's error if it indicates failure, or throws an
|
|
* exception if not.
|
|
*
|
|
* @exception `result::NoError` if the result does not reflect an error state
|
|
*/
|
|
const result::Error& errorOrThrow() const {
|
|
if ( hasValue() )
|
|
throw result::NoError();
|
|
|
|
return error();
|
|
}
|
|
|
|
/** Returns true if the result represents a successful return value. */
|
|
bool hasValue() const { return std::holds_alternative<T>(_value); }
|
|
|
|
/** Returns the result's value, assuming it indicates success. */
|
|
const T& operator*() const { return value(); }
|
|
/** Returns the result's value, assuming it indicates success. */
|
|
T& operator*() { return value(); }
|
|
/** Returns the result's value, assuming it indicates success. */
|
|
const T* operator->() const { return std::get_if<T>(&_value); }
|
|
/** Returns the result's value, assuming it indicates success. */
|
|
T* operator->() { return std::get_if<T>(&_value); }
|
|
|
|
/** Returns true if the result represents a successful return value. */
|
|
explicit operator bool() const { return hasValue(); }
|
|
|
|
/** Converts the result to an optional that's set if it represents a successful return value. */
|
|
operator std::optional<T>() const { return hasValue() ? std::make_optional(value()) : std::nullopt; }
|
|
|
|
Result& operator=(const Result& other) = default;
|
|
Result& operator=(Result&& other) = default; // NOLINT (hicpp-noexcept-move)
|
|
|
|
friend bool operator==(const Result& a, const Result& b) {
|
|
if ( a.hasValue() != b.hasValue() )
|
|
return false;
|
|
|
|
if ( a.hasValue() )
|
|
return a.value() == b.value();
|
|
else
|
|
return a.error() == b.error();
|
|
}
|
|
|
|
friend bool operator!=(const Result& a, const Result& b) { return ! (a == b); }
|
|
|
|
private:
|
|
std::variant<T, result::Error> _value;
|
|
};
|
|
|
|
/** Similar to `std::make_optional`, construct a result from a value. */
|
|
template<typename T>
|
|
Result<T> make_result(T&& t) {
|
|
return Result<T>(std::forward<T>(t));
|
|
}
|
|
|
|
} // namespace hilti::rt
|