#pragma once
#include <memory>
#include <optional>
#include <string_view>
#include "mgclient-value.hpp"
#include "mgclient.h"
namespace mg {
class MgException : public std::exception {
public:
explicit MgException(const std::string_view message) : msg_(message) {}
const char *what() const noexcept override { return msg_.c_str(); }
protected:
std::string msg_;
};
class ClientException : public MgException {
public:
explicit ClientException(const std::string_view message)
: MgException(message) {}
};
class TransientException : public MgException {
public:
explicit TransientException(const std::string_view message)
: MgException(message) {}
};
class DatabaseException : public MgException {
public:
explicit DatabaseException(const std::string_view message)
: MgException(message) {}
};
class Client {
public:
struct Params {
std::string host = "127.0.0.1";
uint16_t port = 7687;
std::string username = "";
std::string password = "";
bool use_ssl = false;
std::string user_agent = "mgclient++/" + std::string(mg_client_version());
};
Client(const Client &) = delete;
Client(Client &&) = default;
Client &operator=(const Client &) = delete;
Client &operator=(Client &&) = delete;
~Client();
static const char *Version();
static int Init();
static void Finalize();
bool Execute(const std::string &statement);
bool Execute(const std::string &statement, const ConstMap ¶ms);
std::optional<std::vector<Value>> FetchOne();
void DiscardAll();
std::optional<std::vector<std::vector<Value>>> FetchAll();
const std::vector<std::string> &GetColumns() const;
bool BeginTransaction();
bool CommitTransaction();
bool RollbackTransaction();
static std::unique_ptr<Client> Connect(const Params ¶ms);
private:
explicit Client(mg_session *session);
mg_session *session_;
std::vector<std::string> columns_;
};
inline std::unique_ptr<Client> Client::Connect(const Client::Params ¶ms) {
mg_session_params *mg_params = mg_session_params_make();
if (!mg_params) {
return nullptr;
}
mg_session_params_set_host(mg_params, params.host.c_str());
mg_session_params_set_port(mg_params, params.port);
if (!params.username.empty()) {
mg_session_params_set_username(mg_params, params.username.c_str());
mg_session_params_set_password(mg_params, params.password.c_str());
}
mg_session_params_set_user_agent(mg_params, params.user_agent.c_str());
mg_session_params_set_sslmode(
mg_params, params.use_ssl ? MG_SSLMODE_REQUIRE : MG_SSLMODE_DISABLE);
mg_session *session = nullptr;
int status = mg_connect(mg_params, &session);
mg_session_params_destroy(mg_params);
if (status < 0) {
return nullptr;
}
return std::unique_ptr<Client>(new Client(session));
}
inline Client::Client(mg_session *session) : session_(session) {}
inline Client::~Client() { mg_session_destroy(session_); }
inline const char *Client::Version() { return mg_client_version(); }
inline int Client::Init() { return mg_init(); }
inline void Client::Finalize() { mg_finalize(); }
inline bool Client::Execute(const std::string &statement) {
const mg_list *columns;
int status = mg_session_run(session_, statement.c_str(), nullptr, nullptr,
&columns, nullptr);
if (status < 0) {
return false;
}
status = mg_session_pull(session_, nullptr);
if (status < 0) {
return false;
}
const size_t list_length = mg_list_size(columns);
columns_.clear();
for (size_t i = 0; i < list_length; i++) {
columns_.push_back(
std::string(Value(mg_list_at(columns, i)).ValueString()));
}
return true;
}
inline bool Client::Execute(const std::string &statement,
const ConstMap ¶ms) {
const mg_list *columns;
int status = mg_session_run(session_, statement.c_str(), params.ptr(),
nullptr, &columns, nullptr);
if (status < 0) {
return false;
}
status = mg_session_pull(session_, nullptr);
if (status < 0) {
return false;
}
const size_t list_length = mg_list_size(columns);
columns_.clear();
for (size_t i = 0; i < list_length; i++) {
columns_.push_back(
std::string(Value(mg_list_at(columns, i)).ValueString()));
}
return true;
}
inline std::optional<std::vector<Value>> Client::FetchOne() {
mg_result *result;
int status = mg_session_fetch(session_, &result);
if (status == MG_ERROR_CLIENT_ERROR) {
throw ClientException(mg_session_error(session_));
}
if (status == MG_ERROR_TRANSIENT_ERROR) {
throw TransientException(mg_session_error(session_));
}
if (status == MG_ERROR_DATABASE_ERROR) {
throw DatabaseException(mg_session_error(session_));
}
if (status != 1) {
return std::nullopt;
}
std::vector<Value> values;
const mg_list *list = mg_result_row(result);
const size_t list_length = mg_list_size(list);
values.reserve(list_length);
for (size_t i = 0; i < list_length; ++i) {
values.emplace_back(Value(mg_list_at(list, i)));
}
return values;
}
inline void Client::DiscardAll() { while (FetchOne()); }
inline std::optional<std::vector<std::vector<Value>>> Client::FetchAll() {
std::vector<std::vector<Value>> data;
while (auto maybe_result = FetchOne()) {
data.emplace_back(std::move(*maybe_result));
}
return data;
}
inline const std::vector<std::string> &Client::GetColumns() const {
return columns_;
}
inline bool Client::BeginTransaction() {
return mg_session_begin_transaction(session_, nullptr) == 0;
}
inline bool Client::CommitTransaction() {
mg_result *result;
return mg_session_commit_transaction(session_, &result) == 0;
}
inline bool Client::RollbackTransaction() {
mg_result *result;
return mg_session_rollback_transaction(session_, &result) == 0;
}
}