diff --git a/include/matador/query/expression_evaluator.hpp b/include/matador/query/expression_evaluator.hpp index 61b318f..4d95a56 100644 --- a/include/matador/query/expression_evaluator.hpp +++ b/include/matador/query/expression_evaluator.hpp @@ -19,7 +19,7 @@ public: void visit(const value_expression& node) override; void visit(const placeholder_expression& node) override; - const std::string& result() const; + [[nodiscard]] const std::string& result() const; private: const sql::dialect &dialect_; diff --git a/include/matador/sql/statement.hpp b/include/matador/sql/statement.hpp index 1e4e1ef..61629f3 100644 --- a/include/matador/sql/statement.hpp +++ b/include/matador/sql/statement.hpp @@ -171,7 +171,8 @@ template utils::result, utils::error> statement::fetch() { std::cout << statement_proxy_->sql() << std::endl; statement_proxy_->statement_->query_.result_type = typeid(Type); - return statement_proxy_->fetch(*bindings_).and_then([this](std::unique_ptr &&value) { + return statement_proxy_->fetch(*bindings_) + .and_then([this](std::unique_ptr &&value) -> utils::result, utils::error> { auto resolver = statement_proxy_->statement_->query_.resolver->object_resolver(); const auto prototype = value->prototype(); return utils::ok(query_result(std::forward(value), resolver, [prototype] { diff --git a/include/matador/utils/result.hpp b/include/matador/utils/result.hpp index e387e53..18c6226 100644 --- a/include/matador/utils/result.hpp +++ b/include/matador/utils/result.hpp @@ -2,7 +2,6 @@ #define QUERY_RESULT_HPP #include -#include #include #include @@ -21,6 +20,7 @@ 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)) {} @@ -55,15 +55,43 @@ 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_(ValueType{}) {} - result(ok value) : result_(std::move(value.release())) {} // NOLINT(*-explicit-constructor) - result(failure error) : result_(std::move(error.release())) {} // NOLINT(*-explicit-constructor) + 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; @@ -71,31 +99,76 @@ public: 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_); } + [[nodiscard]] bool is_ok() const { + return std::holds_alternative>(result_); + } + [[nodiscard]] bool is_error() const { + return std::holds_alternative>(result_); + } - ValueType&& release() { return std::move(std::get(result_)); } - ErrorType&& release_error() { return std::move(std::get(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()); + } - const ValueType& value() const { return std::get(result_); } - ValueType& value() { return std::get(result_); } - const ErrorType& err() const { return std::get(result_); } - ErrorType& err() { return std::get(result_); } + 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(); + } - constexpr const ValueType* operator->() const { return &value(); } - constexpr ValueType* operator->() { return &std::get(result_); } + template + constexpr std::enable_if_t, const T*> operator->() const { + return &value(); + } + template + constexpr std::enable_if_t, T*> operator->() { + return &value(); + } - constexpr const ValueType& operator*() const& noexcept { return value(); } - constexpr ValueType& operator*() & noexcept { 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> + typename SecondValueType = typename detail::map_result_value_type::type> result map(Func &&f) { - if (is_ok()) { - return result(ok(std::invoke(std::forward(f), release()))); + if (is_error()) { + return failure(release_error()); } - return result(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{std::invoke(std::forward(f), release_error())}; } - return ok(release()); + if constexpr (std::is_void_v) { + return ok{}; + } else { + return ok(release()); + } } - template::value_type> - result and_then(Func &&f) { - using ReturnResult = std::invoke_result_t; + template ::type> + ReturnResult and_then(Func &&f) { + static_assert(is_result::value, "and_then() callback must return matador::utils::result"); + if (is_ok()) { - return std::invoke(std::forward(f), release()); + 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())); + return ReturnResult(failure(release_error())); } - template, - typename SecondErrorType = typename FailureType::value_type> + template , + typename SecondErrorType = typename FailureType::value_type> result or_else(Func &&f) { if (is_error()) { return result( @@ -129,69 +211,16 @@ public: ); } - return result(ok(release())); + if constexpr (std::is_void_v) { + return ok{}; + } else { + return ok(release()); + } } private: - std::variant result_; + std::variant, failure> result_; }; - -template < typename ErrorType > -class result -{ -public: - using value_type = void; - using error_type = ErrorType; - - result() = default; - result(ok /*value*/) {} - result(failure error) : result_(std::move(error.release())) {} // 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 !result_.has_value(); } - [[nodiscard]] bool is_error() const { return result_.has_value(); } - - ErrorType&& release_error() { return std::move(*result_); } - - const ErrorType& err() const { return result_.value(); } - ErrorType err() { return result_.value(); } - - template> - result map(Func &&f) { - if (is_ok()) { - return result(ok(f())); - } - - return result(failure(release_error())); - } - - template - result and_then(Func &&f) { - if (is_ok()) { - return f(); - } - - return result(failure(release_error())); - } - - template::value_type> - result or_else(Func &&f) { - if (is_error()) { - return f(release_error()); - } - - return result(ok()); - } - -private: - std::optional result_; -}; - } #endif //QUERY_RESULT_HPP diff --git a/include/matador/utils/value.hpp b/include/matador/utils/value.hpp index f7a5471..2616e6d 100644 --- a/include/matador/utils/value.hpp +++ b/include/matador/utils/value.hpp @@ -56,7 +56,7 @@ public: if (!res.is_ok()) { return std::nullopt; } - return *res; + return res.value(); } template diff --git a/source/orm/query/intermediates/fetchable_query.cpp b/source/orm/query/intermediates/fetchable_query.cpp index 6b25061..b655039 100644 --- a/source/orm/query/intermediates/fetchable_query.cpp +++ b/source/orm/query/intermediates/fetchable_query.cpp @@ -30,7 +30,7 @@ utils::result, utils::error> fetchable_query::fet auto ctx = compiler.build(*context_, exec.dialect(), std::nullopt); ctx.resolver = exec.resolver(); return exec.fetch(ctx) - .and_then([](auto &&res) { + .and_then([](auto &&res) -> utils::result, utils::error> { const auto prototype = res->prototype(); return utils::ok(sql::query_result(std::forward(res), prototype)); }); diff --git a/test/backends/QueryFixture.cpp b/test/backends/QueryFixture.cpp index 9aa84a0..499af7e 100644 --- a/test/backends/QueryFixture.cpp +++ b/test/backends/QueryFixture.cpp @@ -37,7 +37,8 @@ void QueryFixture::check_table_not_exists(const std::string &table_name) const { } void QueryFixture::drop_table_if_exists(const std::string &table_name) const { - const auto result = db.exists(table_name).and_then([&table_name, this](const bool exists) { + const auto result = db.exists(table_name) + .and_then([&table_name, this](const bool exists) -> utils::result { if (exists) { auto res = query::drop() .table(table_name) diff --git a/test/backends/SequenceFixture.cpp b/test/backends/SequenceFixture.cpp index 1bc4e9f..b44d41b 100644 --- a/test/backends/SequenceFixture.cpp +++ b/test/backends/SequenceFixture.cpp @@ -35,7 +35,8 @@ void SequenceFixture::check_sequence_not_exists(const std::string& sequence_name } void SequenceFixture::drop_sequence_if_exists(const std::string& sequence_name) const { - const auto result = db.sequence_exists(sequence_name).and_then([&sequence_name, this](const bool exists) { + const auto result = db.sequence_exists(sequence_name). + and_then([&sequence_name, this](const bool exists) -> utils::result { if (exists) { auto res = query::drop() .sequence(sequence_name)