Compare commits

..
14 changed files with 454 additions and 225 deletions
+103 -38
View File
@@ -26,31 +26,37 @@ public:
// Lazy // Lazy
object_proxy(std::weak_ptr<object_resolver<Type>> resolver, utils::identifier id) object_proxy(std::weak_ptr<object_resolver<Type>> resolver, utils::identifier id)
: resolver_(resolver) : resolver_(std::move(resolver))
, pk_(std::move(id)) { , pk_(std::move(id))
, state_(object_state::Persistent) {
} }
// Eager // Eager
object_proxy(std::weak_ptr<object_resolver<Type>> resolver, std::shared_ptr<Type> obj) object_proxy(std::weak_ptr<object_resolver<Type>> resolver, std::shared_ptr<Type> obj)
: obj_(obj) : obj_(std::move(obj))
, resolver_(resolver) , resolver_(std::move(resolver))
, pk_(primary_key_resolver::resolve_object(*obj).pk) , pk_(obj_ ? primary_key_resolver::resolve_object(*obj_).pk : utils::identifier{})
, state_(object_state::Persistent){ , state_(obj_ ? object_state::Persistent : object_state::Detached) {
} }
// Transient // Transient
explicit object_proxy(std::shared_ptr<Type> obj) explicit object_proxy(std::shared_ptr<Type> obj)
: obj_(obj) : obj_(std::move(obj))
, pk_(primary_key_resolver::resolve_object(*obj).pk) { , pk_(obj_ ? primary_key_resolver::resolve_object(*obj_).pk : utils::identifier{}) {
} }
void attach(std::shared_ptr<Type> obj) { void attach(std::shared_ptr<Type> obj) {
std::lock_guard lock(mutex_); std::lock_guard lock(mutex_);
obj_ = std::move(obj); obj_ = std::move(obj);
if (obj_) { if (!obj_) {
pk_ = primary_key_resolver::resolve_object(*obj_).pk; pk_.clear();
state_.store(object_state::Persistent, std::memory_order_release); state_ = object_state::Detached;
return;
} }
pk_ = primary_key_resolver::resolve_object(*obj_).pk;
state_ = object_state::Persistent;
} }
void resolver(std::weak_ptr<object_resolver<Type>> resolver) { void resolver(std::weak_ptr<object_resolver<Type>> resolver) {
@@ -59,66 +65,125 @@ public:
} }
[[nodiscard]] std::shared_ptr<Type> object() const { [[nodiscard]] std::shared_ptr<Type> object() const {
if (!obj_) { return resolve_object();
std::ignore = resolve();
}
return obj_;
} }
void invalidate() { void invalidate() {
std::lock_guard lock(mutex_); std::lock_guard lock(mutex_);
obj_.reset(); obj_.reset();
resolver_.reset(); resolver_.reset();
state_.store(object_state::Detached, std::memory_order_release); state_ = object_state::Detached;
} }
[[nodiscard]] void *raw_pointer() const { return static_cast<void *>(pointer()); } [[nodiscard]] void *raw_pointer() const { return static_cast<void *>(pointer()); }
Type *operator->() { return pointer(); } Type *operator->() {
Type &operator*() { return *pointer(); } auto *ptr = pointer();
const Type &operator*() const { return *pointer(); } if (!ptr) {
throw std::runtime_error("Cannot dereference empty object proxy");
}
return ptr;
}
Type *pointer() const { return resolve(); } const Type *operator->() const {
auto *ptr = pointer();
if (!ptr) {
throw std::runtime_error("Cannot dereference empty object proxy");
}
return ptr;
}
Type &operator*() {
auto *ptr = pointer();
if (!ptr) {
throw std::runtime_error("Cannot dereference empty object proxy");
}
return *ptr;
}
const Type &operator*() const {
auto *ptr = pointer();
if (!ptr) {
throw std::runtime_error("Cannot dereference empty object proxy");
}
return *ptr;
}
Type *pointer() const {
return resolve_object().get();
}
[[nodiscard]] bool empty() const {
std::lock_guard lock(mutex_);
return !obj_ && resolver_.expired();
}
[[nodiscard]] bool empty() const { return !obj_ && resolver_.expired(); }
[[nodiscard]] bool valid() const { return !empty(); } [[nodiscard]] bool valid() const { return !empty(); }
[[nodiscard]] bool has_primary_key() const { return !pk_.is_null(); }
[[nodiscard]] const utils::identifier &primary_key() const { return pk_; } [[nodiscard]] bool has_primary_key() const {
void primary_key(const utils::identifier &pk) { pk_ = pk; } std::lock_guard lock(mutex_);
return !pk_.is_null();
}
[[nodiscard]] utils::identifier primary_key() const {
std::lock_guard lock(mutex_);
return pk_;
}
void primary_key(const utils::identifier &pk) {
std::lock_guard lock(mutex_);
pk_ = pk;
}
bool is_persistent() const { return is_state(object_state::Persistent); } bool is_persistent() const { return is_state(object_state::Persistent); }
bool is_transient() const { return is_state(object_state::Transient); } bool is_transient() const { return is_state(object_state::Transient); }
bool is_detached() const { return is_state(object_state::Detached); } bool is_detached() const { return is_state(object_state::Detached); }
bool is_removed() const { return is_state(object_state::Removed); } bool is_removed() const { return is_state(object_state::Removed); }
bool is_state(const object_state state) const { return state_ == state; }
bool is_state(const object_state state) const {
std::lock_guard lock(mutex_);
return state_ == state;
}
void change_state(const object_state state) { void change_state(const object_state state) {
state_.store(state, std::memory_order_release); std::lock_guard lock(mutex_);
state_ = state;
} }
private: private:
Type* resolve() const { std::shared_ptr<Type> resolve_object() const {
std::shared_ptr<Type> current;
std::shared_ptr<object_resolver<Type>> resolver;
utils::identifier pk;
{
std::lock_guard lock(mutex_);
if (obj_) { if (obj_) {
return obj_.get(); return obj_;
} }
std::lock_guard lock(mutex_); resolver = resolver_.lock();
auto resolver = resolver_.lock();
if (!resolver) { if (!resolver) {
return nullptr; return nullptr;
// Todo: Add states (Detached, Attached, Transient) - if attached an no resolver is available throw runtime exception
// throw std::runtime_error("Detached proxy (session expired)");
} }
const_cast<std::shared_ptr<Type>&>(obj_) = resolver->resolve(pk_); pk = pk_;
return obj_.get();
} }
current = resolver->resolve(pk);
{
std::lock_guard lock(mutex_);
if (!obj_) {
obj_ = std::move(current);
}
return obj_;
}
}
private: private:
std::shared_ptr<Type> obj_{}; mutable std::shared_ptr<Type> obj_{};
std::weak_ptr<object_resolver<Type>> resolver_{}; mutable std::weak_ptr<object_resolver<Type>> resolver_{};
utils::identifier pk_{}; utils::identifier pk_{};
std::atomic<object_state> state_{object_state::Transient}; object_state state_{object_state::Transient};
mutable std::mutex mutex_{}; mutable std::mutex mutex_{};
}; };
} }
+106 -22
View File
@@ -17,68 +17,152 @@ inline constexpr null_object_ptr_t nullobj{};
template <typename Type> template <typename Type>
class object_ptr { class object_ptr {
public: public:
object_ptr() object_ptr()
: proxy_(std::make_shared<object_proxy<Type>>()) {} : proxy_(std::make_shared<object_proxy<Type>>()) {}
object_ptr(null_object_ptr_t) {} object_ptr(null_object_ptr_t) {}
explicit object_ptr(std::shared_ptr<Type> obj) explicit object_ptr(std::shared_ptr<Type> obj)
: proxy_(std::make_shared<object_proxy<Type>>(obj)) {} : proxy_(std::make_shared<object_proxy<Type>>(std::move(obj))) {}
explicit object_ptr(std::shared_ptr<object_proxy<Type>> obj) explicit object_ptr(std::shared_ptr<object_proxy<Type>> obj)
: proxy_(std::move(obj)) {} : proxy_(std::move(obj)) {}
object_ptr(const object_ptr &other) = default; object_ptr(const object_ptr &other) = default;
object_ptr(object_ptr &&other) noexcept = default; object_ptr(object_ptr &&other) noexcept = default;
object_ptr& operator=(const object_ptr &other) = default; object_ptr& operator=(const object_ptr &other) = default;
object_ptr& operator=(object_ptr &&other) = default; object_ptr& operator=(object_ptr &&other) noexcept = default;
object_ptr& operator=(null_object_ptr_t) { object_ptr& operator=(null_object_ptr_t) {
proxy_.reset(); proxy_.reset();
return *this; return *this;
} }
bool operator==(const object_ptr &other) const { bool operator==(const object_ptr &other) const {
return get() == other.get(); if (proxy_ == other.proxy_) {
return true;
} }
if (!proxy_ || !other.proxy_) {
return false;
}
if (has_primary_key() && other.has_primary_key()) {
return primary_key() == other.primary_key();
}
return false;
}
bool operator==(null_object_ptr_t) const { bool operator==(null_object_ptr_t) const {
return empty(); return empty();
} }
bool operator!=(const object_ptr &other) const { return !operator==(other); } bool operator!=(const object_ptr &other) const { return !operator==(other); }
bool operator!=(null_object_ptr_t) const { return !empty(); } bool operator!=(null_object_ptr_t) const { return !empty(); }
using value_type = Type; using value_type = Type;
Type *operator->() const { return get(); } Type *operator->() const {
Type &operator*() { return *get(); } return checked_get();
const Type &operator*() const { return *get(); } }
[[nodiscard]] bool empty() const { return get() == nullptr; } Type &operator*() {
return *checked_get();
}
const Type &operator*() const {
return *checked_get();
}
[[nodiscard]] bool empty() const {
return proxy_ == nullptr || proxy_->empty();
}
Type *get() const { Type *get() const {
return proxy_ ? proxy_->pointer() : nullptr; return proxy_ ? proxy_->pointer() : nullptr;
} }
void reset() { proxy_.reset(); } [[nodiscard]] std::shared_ptr<Type> object() const {
void reset(const std::shared_ptr<object_proxy<Type>>& proxy) { proxy_ = proxy; } return proxy_ ? proxy_->object() : nullptr;
}
[[nodiscard]] std::shared_ptr<object_proxy<Type>> proxy() const { return proxy_; } void reset() {
proxy_.reset();
}
operator bool() const { return valid(); } void reset(std::shared_ptr<object_proxy<Type>> proxy) {
[[nodiscard]] bool valid() const { return proxy_ != nullptr && !proxy_->empty(); } proxy_ = std::move(proxy);
}
[[nodiscard]] bool has_primary_key() const { return proxy_->has_primary_key(); } [[nodiscard]] std::shared_ptr<object_proxy<Type>> proxy() const {
[[nodiscard]] const utils::identifier &primary_key() const { return proxy_->primary_key(); } return proxy_;
void primary_key(const utils::identifier &pk) { proxy_->primary_key(pk); } }
[[nodiscard]] bool is_persistent() const { return proxy_->is_persistent(); } explicit operator bool() const {
[[nodiscard]] bool is_transient() const { return proxy_->is_transient(); } return valid();
[[nodiscard]] bool is_detached() const { return proxy_->is_detached(); } }
[[nodiscard]] bool is_removed() const { return proxy_->is_removed(); }
[[nodiscard]] bool is_state(const object_state state) const { return proxy_->is_state(state); } [[nodiscard]] bool valid() const {
return proxy_ != nullptr && !proxy_->empty();
}
[[nodiscard]] bool has_primary_key() const {
return proxy_ != nullptr && proxy_->has_primary_key();
}
[[nodiscard]] utils::identifier primary_key() const {
return proxy_ ? proxy_->primary_key() : utils::identifier{};
}
void primary_key(const utils::identifier &pk) {
ensure_proxy();
proxy_->primary_key(pk);
}
[[nodiscard]] bool is_persistent() const {
return proxy_ != nullptr && proxy_->is_persistent();
}
[[nodiscard]] bool is_transient() const {
return proxy_ != nullptr && proxy_->is_transient();
}
[[nodiscard]] bool is_detached() const {
return proxy_ != nullptr && proxy_->is_detached();
}
[[nodiscard]] bool is_removed() const {
return proxy_ != nullptr && proxy_->is_removed();
}
[[nodiscard]] bool is_state(const object_state state) const {
return proxy_ != nullptr && proxy_->is_state(state);
}
void change_state(object_state s) const { void change_state(object_state s) const {
if (proxy_) { if (proxy_) {
proxy_->change_state(s); proxy_->change_state(s);
} }
} }
private: private:
std::shared_ptr<object_proxy<Type> > proxy_{}; Type *checked_get() const {
auto *ptr = get();
if (!ptr) {
throw std::runtime_error("Cannot dereference empty object_ptr");
}
return ptr;
}
void ensure_proxy() {
if (!proxy_) {
proxy_ = std::make_shared<object_proxy<Type>>();
}
}
private:
std::shared_ptr<object_proxy<Type>> proxy_{};
}; };
template<typename> template<typename>
@@ -19,7 +19,7 @@ public:
void visit(const value_expression& node) override; void visit(const value_expression& node) override;
void visit(const placeholder_expression& node) override; void visit(const placeholder_expression& node) override;
const std::string& result() const; [[nodiscard]] const std::string& result() const;
private: private:
const sql::dialect &dialect_; const sql::dialect &dialect_;
+2 -1
View File
@@ -171,7 +171,8 @@ template<class Type>
utils::result<query_result<Type>, utils::error> statement::fetch() { utils::result<query_result<Type>, utils::error> statement::fetch() {
std::cout << statement_proxy_->sql() << std::endl; std::cout << statement_proxy_->sql() << std::endl;
statement_proxy_->statement_->query_.result_type = typeid(Type); statement_proxy_->statement_->query_.result_type = typeid(Type);
return statement_proxy_->fetch(*bindings_).and_then([this](std::unique_ptr<query_result_impl> &&value) { return statement_proxy_->fetch(*bindings_)
.and_then([this](std::unique_ptr<query_result_impl> &&value) -> utils::result<query_result<Type>, utils::error> {
auto resolver = statement_proxy_->statement_->query_.resolver->object_resolver<Type>(); auto resolver = statement_proxy_->statement_->query_.resolver->object_resolver<Type>();
const auto prototype = value->prototype(); const auto prototype = value->prototype();
return utils::ok(query_result<Type>(std::forward<decltype(value)>(value), resolver, [prototype] { return utils::ok(query_result<Type>(std::forward<decltype(value)>(value), resolver, [prototype] {
+131 -98
View File
@@ -2,7 +2,6 @@
#define QUERY_RESULT_HPP #define QUERY_RESULT_HPP
#include <variant> #include <variant>
#include <optional>
#include <functional> #include <functional>
#include <type_traits> #include <type_traits>
@@ -21,6 +20,7 @@ template < typename ValueType >
class ok { class ok {
public: public:
using value_type = ValueType; using value_type = ValueType;
constexpr ok() = default;
explicit constexpr ok(const ValueType &value) : value_(value) {} explicit constexpr ok(const ValueType &value) : value_(value) {}
explicit constexpr ok(ValueType &&value) : value_(std::move(value)) {} explicit constexpr ok(ValueType &&value) : value_(std::move(value)) {}
@@ -49,21 +49,49 @@ public:
constexpr ErrorType&& release() { return std::move(error_); } constexpr ErrorType&& release() { return std::move(error_); }
const ErrorType& value() const { return error_; } const ErrorType& value() const { return error_; }
ErrorType value() { return error_; } ErrorType& value() { return error_; }
private: private:
ErrorType error_; ErrorType error_;
}; };
namespace detail {
template <typename ValueType, typename Func, bool IsVoidValue = std::is_void_v<ValueType>>
struct map_result_value_type;
template <typename ValueType, typename Func>
struct map_result_value_type<ValueType, Func, false> {
using type = std::invoke_result_t<Func, ValueType&&>;
};
template <typename ValueType, typename Func>
struct map_result_value_type<ValueType, Func, true> {
using type = std::invoke_result_t<Func>;
};
template <typename ValueType, typename Func, bool IsVoidValue = std::is_void_v<ValueType>>
struct and_then_result_type;
template <typename ValueType, typename Func>
struct and_then_result_type<ValueType, Func, false> {
using type = std::invoke_result_t<Func, ValueType&&>;
};
template <typename ValueType, typename Func>
struct and_then_result_type<ValueType, Func, true> {
using type = std::invoke_result_t<Func>;
};
}
template < typename ValueType, typename ErrorType > template < typename ValueType, typename ErrorType >
class result { class result {
public: public:
using value_type = ValueType; using value_type = ValueType;
using error_type = ErrorType; using error_type = ErrorType;
result() : result_(ValueType{}) {} result() : result_(ok<value_type>{}) {}
result(ok<value_type> value) : result_(std::move(value.release())) {} // NOLINT(*-explicit-constructor) result(ok<value_type> value) : result_(std::move(value)) {} // NOLINT(*-explicit-constructor)
result(failure<error_type> error) : result_(std::move(error.release())) {} // NOLINT(*-explicit-constructor) result(failure<error_type> error) : result_(std::move(error)) {} // NOLINT(*-explicit-constructor)
result(const result &x) = default; result(const result &x) = default;
result& operator=(const result &x) = default; result& operator=(const result &x) = default;
result(result &&x) = default; result(result &&x) = default;
@@ -71,123 +99,128 @@ public:
operator bool() const { return is_ok(); } // NOLINT(*-explicit-constructor) operator bool() const { return is_ok(); } // NOLINT(*-explicit-constructor)
[[nodiscard]] bool is_ok() const { return std::holds_alternative<value_type>(result_); } [[nodiscard]] bool is_ok() const {
[[nodiscard]] bool is_error() const { return std::holds_alternative<error_type>(result_); } return std::holds_alternative<ok<value_type>>(result_);
}
ValueType&& release() { return std::move(std::get<value_type>(result_)); } [[nodiscard]] bool is_error() const {
ErrorType&& release_error() { return std::move(std::get<error_type>(result_)); } return std::holds_alternative<failure<error_type>>(result_);
const ValueType& value() const { return std::get<value_type>(result_); }
ValueType& value() { return std::get<value_type>(result_); }
const ErrorType& err() const { return std::get<error_type>(result_); }
ErrorType err() { return std::get<error_type>(result_); }
constexpr const ValueType* operator->() const { return &value(); }
constexpr ValueType* operator->() { return &std::get<value_type>(result_); }
constexpr const ValueType& operator*() const& noexcept { return value(); }
constexpr ValueType& operator*() & noexcept { return value(); }
template<typename Func,
typename SecondValueType = std::invoke_result_t<Func, ValueType >>
result<SecondValueType, ErrorType> map(Func &&f) {
if (is_ok()) {
return result<SecondValueType, ErrorType>(ok(f(release())));
} }
return result<SecondValueType, ErrorType>(failure(release_error())); template <typename T = ValueType>
std::enable_if_t<!std::is_void_v<T>, T&&> release() {
return std::move(std::get<ok<value_type>>(result_).release());
}
ErrorType&& release_error() {
return std::move(std::get<failure<error_type>>(result_).release());
}
template <typename T = ValueType>
std::enable_if_t<!std::is_void_v<T>, const T&> value() const {
return std::get<ok<value_type>>(result_).value();
}
template <typename T = ValueType>
std::enable_if_t<!std::is_void_v<T>, T&> value() {
return std::get<ok<value_type>>(result_).value();
}
const ErrorType& err() const {
return std::get<failure<error_type>>(result_).value();
}
ErrorType& err() {
return std::get<failure<error_type>>(result_).value();
}
template <typename T = ValueType>
constexpr std::enable_if_t<!std::is_void_v<T>, const T*> operator->() const {
return &value();
}
template <typename T = ValueType>
constexpr std::enable_if_t<!std::is_void_v<T>, T*> operator->() {
return &value();
}
template <typename T = ValueType>
constexpr std::enable_if_t<!std::is_void_v<T>, const T&> operator*() const& noexcept {
return value();
}
template <typename T = ValueType>
constexpr std::enable_if_t<!std::is_void_v<T>, T&> operator*() & noexcept {
return value();
}
template<typename Func,
typename SecondValueType = typename detail::map_result_value_type<ValueType, Func>::type>
result<SecondValueType, ErrorType> map(Func &&f) {
if (is_error()) {
return failure<ErrorType>(release_error());
}
if constexpr (std::is_void_v<ValueType>) {
if constexpr (std::is_void_v<SecondValueType>) {
std::invoke(std::forward<Func>(f));
return ok<void>{};
} else {
return ok<SecondValueType>(std::invoke(std::forward<Func>(f)));
}
} else {
if constexpr (std::is_void_v<SecondValueType>) {
std::invoke(std::forward<Func>(f), release());
return ok<void>{};
} else {
return ok<SecondValueType>(std::invoke(std::forward<Func>(f), release()));
}
}
} }
template<typename Func, template<typename Func,
typename SecondErrorType = typename std::invoke_result_t<Func, ErrorType >::value_type> typename SecondErrorType = typename std::invoke_result_t<Func, ErrorType >::value_type>
result<SecondErrorType, ErrorType> map_error(Func &&f) { result<SecondErrorType, ErrorType> map_error(Func &&f) {
if (!is_ok()) { if (!is_error()) {
return result<SecondErrorType, ErrorType>(ok(release())); return failure<SecondErrorType>{std::invoke(std::forward<Func>(f), release_error())};
} }
return result<SecondErrorType, ErrorType>(error(release_error())); if constexpr (std::is_void_v<ValueType>) {
return ok<void>{};
} else {
return ok<ValueType>(release());
}
} }
template<typename Func, template <typename Func,
typename SecondValueType = typename std::invoke_result_t<Func, ValueType>::value_type> typename ReturnResult = typename detail::and_then_result_type<ValueType, Func>::type>
result<SecondValueType, ErrorType> and_then(Func &&f) { ReturnResult and_then(Func &&f) {
static_assert(is_result<ReturnResult>::value, "and_then() callback must return matador::utils::result");
if (is_ok()) { if (is_ok()) {
return f(release()); if constexpr (std::is_void_v<ValueType>) {
return std::invoke(std::forward<Func>(f));
} else {
return std::invoke(std::forward<Func>(f), release());
}
} }
return result<SecondValueType, ErrorType>(failure(release_error())); return ReturnResult(failure<ErrorType>(release_error()));
} }
template<typename Func, template <typename Func,
typename SecondErrorType = typename std::invoke_result_t<Func, ErrorType&& >::value_type> typename FailureType = std::invoke_result_t<Func, ErrorType&&>,
typename SecondErrorType = typename FailureType::value_type>
result<ValueType, SecondErrorType> or_else(Func &&f) { result<ValueType, SecondErrorType> or_else(Func &&f) {
if (is_error()) { if (is_error()) {
return f(release_error()); return result<ValueType, SecondErrorType>(
std::invoke(std::forward<Func>(f), release_error())
);
} }
return result<ValueType, SecondErrorType>(ok(release())); if constexpr (std::is_void_v<ValueType>) {
return ok<void>{};
} else {
return ok<ValueType>(release());
}
} }
private: private:
std::variant<value_type, error_type> result_; std::variant<ok<value_type>, failure<error_type>> result_;
}; };
template < typename ErrorType >
class result<void, ErrorType>
{
public:
using value_type = void;
using error_type = ErrorType;
result() = default;
result(ok<void> /*value*/) {}
result(failure<error_type> 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<typename Func, typename SecondValueType = std::invoke_result_t<Func>>
result<SecondValueType, ErrorType> map(Func &&f) {
if (is_ok()) {
return result<SecondValueType, ErrorType>(ok(f()));
}
return result<SecondValueType, ErrorType>(failure(release_error()));
}
template<typename Func>
result and_then(Func &&f) {
if (is_ok()) {
return f();
}
return result(failure(release_error()));
}
template<typename Func, typename SecondErrorType = typename std::invoke_result_t<Func, ErrorType&& >::value_type>
result<void, SecondErrorType> or_else(Func &&f) {
if (is_error()) {
return f(release_error());
}
return result<void, SecondErrorType>(ok<void>());
}
private:
std::optional<error_type> result_;
};
} }
#endif //QUERY_RESULT_HPP #endif //QUERY_RESULT_HPP
+1 -1
View File
@@ -56,7 +56,7 @@ public:
if (!res.is_ok()) { if (!res.is_ok()) {
return std::nullopt; return std::nullopt;
} }
return *res; return res.value();
} }
template<class Type> template<class Type>
+3 -3
View File
@@ -3,16 +3,16 @@
#include "matador/utils/result.hpp" #include "matador/utils/result.hpp"
#include "matador/utils/error.hpp" #include "matador/utils/error.hpp"
#include "matador/utils/export.hpp"
#include <string> #include <string>
#include <ostream>
namespace matador::utils { namespace matador::utils {
class version { class MATADOR_UTILS_API version final {
public: public:
version() = default; version() = default;
~version() =default; ~version() = default;
version(unsigned int major, unsigned int minor, unsigned int patch); version(unsigned int major, unsigned int minor, unsigned int patch);
version(const version& x) = default; version(const version& x) = default;
version& operator=(const version& x) = default; version& operator=(const version& x) = default;
+65 -41
View File
@@ -3,98 +3,122 @@
#include <matador/utils/errors.hpp> #include <matador/utils/errors.hpp>
namespace matador::utils { namespace matador::utils {
namespace {
version::version(unsigned int major, unsigned int minor, unsigned int patch) bool parse_uint_component(const std::string &text, std::size_t &pos, unsigned int &value) {
if (pos >= text.size() || text[pos] < '0' || text[pos] > '9') {
return false;
}
unsigned int result{};
while (pos < text.size() && text[pos] >= '0' && text[pos] <= '9') {
const auto digit = static_cast<unsigned int>(text[pos] - '0');
if (result > (std::numeric_limits<unsigned int>::max() - digit) / 10) {
return false;
}
result = result * 10 + digit;
++pos;
}
value = result;
return true;
}
bool parse_dot(const std::string &text, std::size_t &pos) {
if (pos >= text.size() || text[pos] != '.') {
return false;
}
++pos;
return true;
}
} // namespace
version::version(const unsigned int major, const unsigned int minor, const unsigned int patch)
: major_(major) : major_(major)
, minor_(minor) , minor_(minor)
, patch_(patch) , patch_(patch) {
{} }
bool version::operator==(const version &x) const bool version::operator==(const version& x) const {
{
return major_ == x.major_ && return major_ == x.major_ &&
minor_ == x.minor_ && minor_ == x.minor_ &&
patch_ == x.patch_; patch_ == x.patch_;
} }
bool version::operator!=(const version &x) const bool version::operator!=(const version& x) const {
{
return !(*this == x); return !(*this == x);
} }
bool version::operator>(const version &x) const bool version::operator>(const version& x) const {
{
return !(*this <= x); return !(*this <= x);
} }
bool version::operator>=(const version &x) const bool version::operator>=(const version& x) const {
{
return !(*this < x); return !(*this < x);
} }
bool version::operator<(const version &x) const bool version::operator<(const version& x) const {
{
return (major_ < x.major_) || return (major_ < x.major_) ||
(major_ == x.major_ && minor_ < x.minor_) || (major_ == x.major_ && minor_ < x.minor_) ||
(major_ == x.major_ && minor_ == x.minor_ && patch_ < x.patch_); (major_ == x.major_ && minor_ == x.minor_ && patch_ < x.patch_);
} }
bool version::operator<=(const version &x) const bool version::operator<=(const version& x) const {
{
return *this < x || *this == x; return *this < x || *this == x;
} }
std::string version::str() const std::string version::str() const {
{ return std::to_string(major_) + "." +
char buf[32]; std::to_string(minor_) + "." +
sprintf(buf, "%d.%d.%d", major_, minor_, patch_); std::to_string(patch_);
return buf;
} }
std::ostream &operator<<(std::ostream &out, const version &v) std::ostream& operator<<(std::ostream& out, const version& v) {
{
out << v.str(); out << v.str();
return out; return out;
} }
result<version, error> version::from_string(const std::string &version_string) result<version, error> version::from_string(const std::string& version_string) {
{ unsigned int major{};
version result; unsigned int minor{};
if (const auto ret = sscanf(version_string.c_str(), "%u.%u.%u", &result.major_, &result.minor_, &result.patch_); ret != 3) { unsigned int patch{};
if (std::size_t pos{}; !parse_uint_component(version_string, pos, major) ||
!parse_dot(version_string, pos) ||
!parse_uint_component(version_string, pos, minor) ||
!parse_dot(version_string, pos) ||
!parse_uint_component(version_string, pos, patch) ||
pos != version_string.size()) {
return failure(error(utils_error::InvalidVersionString, version_string)); return failure(error(utils_error::InvalidVersionString, version_string));
} }
return ok(result); return ok(version{major, minor, patch});}
}
unsigned int version::major() const unsigned int version::major() const {
{
return major_; return major_;
} }
unsigned int version::minor() const unsigned int version::minor() const {
{
return minor_; return minor_;
} }
unsigned int version::patch() const unsigned int version::patch() const {
{
return patch_; return patch_;
} }
void version::major(unsigned int m) void version::major(unsigned int m) {
{
major_ = m; major_ = m;
} }
void version::minor(unsigned int m) void version::minor(unsigned int m) {
{
minor_ = m; minor_ = m;
} }
void version::patch(unsigned int p) void version::patch(unsigned int p) {
{
patch_ = p; patch_ = p;
} }
} }
-1
View File
@@ -71,7 +71,6 @@ add_library(matador-orm STATIC
../../include/matador/query/key_value_generator.hpp ../../include/matador/query/key_value_generator.hpp
../../include/matador/query/manual_pk_generator.hpp ../../include/matador/query/manual_pk_generator.hpp
../../include/matador/query/meta_table_macro.hpp ../../include/matador/query/meta_table_macro.hpp
../../include/matador/query/meta_table_macro.hpp
../../include/matador/query/query.hpp ../../include/matador/query/query.hpp
../../include/matador/query/query_builder.hpp ../../include/matador/query/query_builder.hpp
../../include/matador/query/query_builder_utils.hpp ../../include/matador/query/query_builder_utils.hpp
@@ -30,7 +30,7 @@ utils::result<sql::query_result<sql::record>, utils::error> fetchable_query::fet
auto ctx = compiler.build(*context_, exec.dialect(), std::nullopt); auto ctx = compiler.build(*context_, exec.dialect(), std::nullopt);
ctx.resolver = exec.resolver(); ctx.resolver = exec.resolver();
return exec.fetch(ctx) return exec.fetch(ctx)
.and_then([](auto &&res) { .and_then([](auto &&res) -> utils::result<sql::query_result<sql::record>, utils::error> {
const auto prototype = res->prototype(); const auto prototype = res->prototype();
return utils::ok(sql::query_result<sql::record>(std::forward<decltype(res)>(res), prototype)); return utils::ok(sql::query_result<sql::record>(std::forward<decltype(res)>(res), prototype));
}); });
+2 -1
View File
@@ -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 { 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<bool, utils::error> {
if (exists) { if (exists) {
auto res = query::drop() auto res = query::drop()
.table(table_name) .table(table_name)
+2 -1
View File
@@ -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 { 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<bool, utils::error> {
if (exists) { if (exists) {
auto res = query::drop() auto res = query::drop()
.sequence(sequence_name) .sequence(sequence_name)
+7 -5
View File
@@ -5,13 +5,15 @@
#include "matador/utils/result.hpp" #include "matador/utils/result.hpp"
namespace matador::test { namespace matador::test {
namespace {
enum class math_error : int32_t { enum class math_error : int32_t {
OK = 0, OK = 0,
DIVISION_BY_ZERO = 1, DIVISION_BY_ZERO = 1,
FAILURE = 2 FAILURE = 2
}; };
}
utils::result<float, math_error>divide(const float x, const float y) { static utils::result<float, math_error>divide(const float x, const float y) {
if (y == 0) { if (y == 0) {
return utils::failure(math_error::DIVISION_BY_ZERO); return utils::failure(math_error::DIVISION_BY_ZERO);
} }
@@ -19,15 +21,15 @@ utils::result<float, math_error>divide(const float x, const float y) {
return utils::ok(x / y); return utils::ok(x / y);
} }
utils::result<float, math_error>multiply(const float x, const float y) { static utils::result<float, math_error>multiply(const float x, const float y) {
return utils::ok(x * y); return utils::ok(x * y);
} }
utils::result<float, math_error>plus(const float x, const float y) { static utils::result<float, math_error>plus(const float x, const float y) {
return utils::ok(x + y); return utils::ok(x + y);
} }
utils::result<void, math_error>action_on_greater_42(const float i) { static utils::result<void, math_error>action_on_greater_42(const float i) {
if (i > 42) { if (i > 42) {
return utils::ok<void>(); return utils::ok<void>();
} }
@@ -88,7 +90,7 @@ TEST_CASE("Test result", "[result]") {
REQUIRE(!res2.is_ok()); REQUIRE(!res2.is_ok());
REQUIRE(res2.is_error()); REQUIRE(res2.is_error());
const auto e = res2.err(); const auto e = res2.err();
// REQUIRE(res2.err() == "division by zero error"); REQUIRE(res2.err() == "division by zero error");
res = test::divide(4, 2) res = test::divide(4, 2)
.and_then([](const auto &val) { return test::multiply(val, 5); }) .and_then([](const auto &val) { return test::multiply(val, 5); })
+22 -3
View File
@@ -4,7 +4,26 @@
using namespace matador::utils; using namespace matador::utils;
TEST_CASE("Test version interface", "[version][interface]") { TEST_CASE("Version: Test form string", "[version][from_string]") {
REQUIRE(version::from_string("1.2.3").is_ok());
REQUIRE(version::from_string("0.0.0").is_ok());
REQUIRE(version::from_string("1.2").is_error());
REQUIRE(version::from_string("1.2.3.4").is_error());
REQUIRE(version::from_string("1.2.3abc").is_error());
REQUIRE(version::from_string("abc").is_error());
REQUIRE(version::from_string("-1.2.3").is_error());
}
TEST_CASE("Version: Test comparison", "[version][comparison]") {
REQUIRE(version(1, 2, 3) == version(1, 2, 3));
REQUIRE(version(1, 2, 3) < version(1, 2, 4));
REQUIRE(version(1, 2, 3) < version(1, 3, 0));
REQUIRE(version(1, 2, 3) < version(2, 0, 0));
REQUIRE(version(2, 0, 0) > version(1, 9, 9));
}
TEST_CASE("Version: Test interface", "[version][interface]") {
version v0; version v0;
REQUIRE(v0.major() == 0); REQUIRE(v0.major() == 0);
@@ -38,8 +57,8 @@ TEST_CASE("Test version interface", "[version][interface]") {
REQUIRE(v2 == v1); REQUIRE(v2 == v1);
} }
TEST_CASE("Test version parsing", "[version][parse]") { TEST_CASE("Version: Test parsing", "[version][parse]") {
const auto version_str{"13.67.34"}; constexpr auto version_str{"13.67.34"};
const auto v1 = version::from_string(version_str); const auto v1 = version::from_string(version_str);
REQUIRE(v1.is_ok()); REQUIRE(v1.is_ok());