#ifndef QUERY_RESULT_HPP #define QUERY_RESULT_HPP #include #include #include namespace matador::utils { template < typename ValueType, typename ErrorType > class [[nodiscard]] result; template struct is_result : std::false_type {}; template < typename ValueType, typename ErrorType > struct is_result> : std::true_type {}; template < typename ValueType > class ok { public: using value_type = ValueType; constexpr ok() = default; explicit constexpr ok(const ValueType &value) : value_(value) {} explicit constexpr ok(ValueType &&value) : value_(std::move(value)) {} constexpr ValueType&& release() { return std::move(value_); } const ValueType& value() const { return value_; } ValueType& value() { return value_; } private: ValueType value_; }; template <> class ok { public: using value_type = void; explicit constexpr ok() = default; }; template < typename ErrorType > class failure { public: using value_type = ErrorType; explicit constexpr failure(const ErrorType &error) : error_(error) {} explicit constexpr failure(ErrorType &&error) : error_(std::move(error)) {} constexpr ErrorType&& release() { return std::move(error_); } const ErrorType& value() const { return error_; } ErrorType& value() { return error_; } private: ErrorType error_; }; namespace detail { template > struct map_result_value_type; template struct map_result_value_type { using type = std::invoke_result_t; }; template struct map_result_value_type { using type = std::invoke_result_t; }; template > struct and_then_result_type; template struct and_then_result_type { using type = std::invoke_result_t; }; template struct and_then_result_type { using type = std::invoke_result_t; }; } template < typename ValueType, typename ErrorType > class result { public: using value_type = ValueType; using error_type = ErrorType; result() : result_(ok{}) {} result(ok value) : result_(std::move(value)) {} // NOLINT(*-explicit-constructor) result(failure error) : result_(std::move(error)) {} // NOLINT(*-explicit-constructor) result(const result &x) = default; result& operator=(const result &x) = default; result(result &&x) = default; result& operator=(result &&x) = default; operator bool() const { return is_ok(); } // NOLINT(*-explicit-constructor) [[nodiscard]] bool is_ok() const { return std::holds_alternative>(result_); } [[nodiscard]] bool is_error() const { return std::holds_alternative>(result_); } template std::enable_if_t, T&&> release() { return std::move(std::get>(result_).release()); } ErrorType&& release_error() { return std::move(std::get>(result_).release()); } template std::enable_if_t, const T&> value() const { return std::get>(result_).value(); } template std::enable_if_t, T&> value() { return std::get>(result_).value(); } const ErrorType& err() const { return std::get>(result_).value(); } ErrorType& err() { return std::get>(result_).value(); } template constexpr std::enable_if_t, const T*> operator->() const { return &value(); } template constexpr std::enable_if_t, T*> operator->() { return &value(); } template constexpr std::enable_if_t, const T&> operator*() const& noexcept { return value(); } template constexpr std::enable_if_t, T&> operator*() & noexcept { return value(); } template::type> result map(Func &&f) { if (is_error()) { return failure(release_error()); } if constexpr (std::is_void_v) { if constexpr (std::is_void_v) { std::invoke(std::forward(f)); return ok{}; } else { return ok(std::invoke(std::forward(f))); } } else { if constexpr (std::is_void_v) { std::invoke(std::forward(f), release()); return ok{}; } else { return ok(std::invoke(std::forward(f), release())); } } } template::value_type> result map_error(Func &&f) { if (!is_error()) { return failure{std::invoke(std::forward(f), release_error())}; } if constexpr (std::is_void_v) { return ok{}; } else { return ok(release()); } } template ::type> ReturnResult and_then(Func &&f) { static_assert(is_result::value, "and_then() callback must return matador::utils::result"); if (is_ok()) { if constexpr (std::is_void_v) { return std::invoke(std::forward(f)); } else { return std::invoke(std::forward(f), release()); } } return ReturnResult(failure(release_error())); } template , typename SecondErrorType = typename FailureType::value_type> result or_else(Func &&f) { if (is_error()) { return result( std::invoke(std::forward(f), release_error()) ); } if constexpr (std::is_void_v) { return ok{}; } else { return ok(release()); } } private: std::variant, failure> result_; }; } #endif //QUERY_RESULT_HPP