5#ifndef MOSTLYHARMLESS_DATABASESTATE_H
6#define MOSTLYHARMLESS_DATABASESTATE_H
8#include <mostly_harmless/mostlyharmless_Concepts.h>
9#include <mostly_harmless/utils/mostlyharmless_Macros.h>
30 template <DatabaseStorageType T>
31 auto databaseQueryCallback(
void* ud,
int count,
char**
data,
char** ) ->
int {
32 auto* result =
static_cast<std::optional<T>*
>(ud);
36 if constexpr (std::same_as<T, std::string>) {
37 *result =
data[Index::TextIndex];
38 }
else if constexpr (std::same_as<T, bool>) {
39 *result =
static_cast<T
>(std::stoi(
data[Index::BoolIndex]));
40 }
else if constexpr (std::is_integral_v<T>) {
41 *result =
static_cast<T
>(std::stoi(
data[Index::IntIndex]));
42 }
else if constexpr (std::same_as<T, float>) {
43 *result = std::stof(
data[Index::FloatIndex]);
44 }
else if constexpr (std::same_as<T, double>) {
45 *result = std::stod(
data[Index::DoubleIndex]);
67 class DatabaseState final {
75 DatabaseState(Private,
const std::filesystem::path& location,
const std::vector<std::pair<std::string, DatabaseValueVariant>>& initialValues) {
76 const auto checkResult = [](
int response) ->
void {
77 if (response != SQLITE_OK) {
78 throw std::exception{};
82 if (!std::filesystem::exists(location.parent_path()) && location.string() !=
":memory:") {
83 throw std::exception{};
86 if (sqlite3_open_v2(location.string().c_str(), &m_databaseHandle, SQLITE_OPEN_READWRITE,
nullptr) != SQLITE_OK) {
87 checkResult(sqlite3_open(location.string().c_str(), &m_databaseHandle));
90 checkResult(sqlite3_exec(m_databaseHandle,
"PRAGMA journal_mode=WAL",
nullptr,
nullptr,
nullptr));
92 checkResult(sqlite3_exec(m_databaseHandle,
93 "CREATE TABLE IF NOT EXISTS DATA (NAME text UNIQUE, TEXT_VALUE text, BOOL_VALUE bool, INT_VALUE int, FLOAT_VALUE float, DOUBLE_VALUE double);",
98 for (
const auto& [key, value] : initialValues) {
99 std::visit([
this, &key](
auto&& arg) {
100 using T = std::decay_t<
decltype(arg)>;
104 set(key, std::forward<
decltype(arg)>(arg));
120 std::swap(m_databaseHandle, other.m_databaseHandle);
128 DatabaseState&
operator=(
const DatabaseState& ) =
delete;
135 DatabaseState&
operator=(DatabaseState&& other)
noexcept {
136 if (
this != &other) {
137 std::swap(m_databaseHandle, other.m_databaseHandle);
152 [[nodiscard]]
static auto tryCreate(
const std::filesystem::path& location,
const std::vector<std::pair<std::string, DatabaseValueVariant>>& initialValues) -> std::optional<DatabaseState> {
154 DatabaseState state{ {}, location, initialValues };
155 return std::move(state);
166 if (!m_databaseHandle)
return;
167 sqlite3_close(m_databaseHandle);
176 template <DatabaseStorageType T>
177 auto set(std::string_view name,
const T& toSet) ->
void {
179 std::string textValue{};
180 bool boolValue{
false };
182 float floatValue{ 0.0f };
183 double doubleValue{ 0.0 };
186 std::string updateStr;
187 if constexpr (std::same_as<T, std::string>) {
188 props.textValue = toSet;
189 updateStr = fmt::format(
"TEXT_VALUE = \"{}\"", props.textValue);
190 }
else if constexpr (std::same_as<T, bool>) {
191 props.boolValue = toSet;
192 updateStr = fmt::format(
"BOOL_VALUE = {}", props.boolValue);
193 }
else if constexpr (std::is_integral_v<T>) {
194 props.intValue =
static_cast<int>(toSet);
195 updateStr = fmt::format(
"INT_VALUE = {}", props.intValue);
196 }
else if constexpr (std::same_as<T, float>) {
197 props.floatValue = toSet;
198 updateStr = fmt::format(
"FLOAT_VALUE = {}", props.floatValue);
199 }
else if constexpr (std::same_as<T, double>) {
200 props.doubleValue = toSet;
201 updateStr = fmt::format(
"DOUBLE_VALUE = {}", props.doubleValue);
204 std::stringstream sstream;
205 sstream <<
"INSERT INTO DATA (NAME, TEXT_VALUE, BOOL_VALUE, INT_VALUE, FLOAT_VALUE, DOUBLE_VALUE)\n";
206 sstream <<
" VALUES ( \"" << name <<
"\", \"" << props.textValue <<
"\", " << props.boolValue <<
"," << props.intValue <<
", " << props.floatValue <<
", " << props.doubleValue <<
" )\n";
207 sstream <<
" ON CONFLICT(NAME) DO UPDATE SET " << updateStr <<
";";
208 const auto execResult = sqlite3_exec(m_databaseHandle, sstream.str().c_str(),
nullptr,
nullptr,
nullptr);
209 if (execResult != SQLITE_OK) {
210 MH_LOG(sqlite3_errmsg(m_databaseHandle));
221 template <DatabaseStorageType T>
222 [[nodiscard]]
auto get(std::string_view name) -> std::optional<T> {
223 std::optional<T> result{};
224 const auto execStr = fmt::format(
"SELECT * FROM DATA WHERE NAME = \"{}\"", name);
225 const auto getRes = sqlite3_exec(m_databaseHandle,
227 databaseQueryCallback<T>,
228 static_cast<void*
>(&result),
230 if (getRes != SQLITE_OK) {
231 MH_LOG(sqlite3_errmsg(m_databaseHandle));
245 [[nodiscard]]
auto duplicate() const -> std::optional<DatabaseState> {
246 const auto filenameOpt = getDatabaseFilename();
250 const std::filesystem::path databasePath{ *filenameOpt };
251 if (!std::filesystem::exists(databasePath)) {
258 [[nodiscard]]
auto getDatabaseFilename() const noexcept -> std::optional<std::
string> {
259 const auto* res = sqlite3_db_filename(m_databaseHandle,
"main");
260 if (!res || strcmp(res,
"") == 0) {
263 return std::string{ res };
265 sqlite3* m_databaseHandle{
nullptr };
static auto tryCreate(const std::filesystem::path &location, const std::vector< std::pair< std::string, DatabaseValueVariant > > &initialValues) -> std::optional< DatabaseState >
Definition mostlyharmless_DatabaseState.h:152
~DatabaseState() noexcept
Definition mostlyharmless_DatabaseState.h:165
DatabaseState & operator=(const DatabaseState &)=delete
DatabaseState & operator=(DatabaseState &&other) noexcept
Definition mostlyharmless_DatabaseState.h:135
DatabaseState(const DatabaseState &)=delete
DatabaseState(DatabaseState &&other) noexcept
Definition mostlyharmless_DatabaseState.h:119
auto set(std::string_view name, const T &toSet) -> void
Definition mostlyharmless_DatabaseState.h:177
auto duplicate() const -> std::optional< DatabaseState >
Definition mostlyharmless_DatabaseState.h:245
auto get(std::string_view name) -> std::optional< T >
Definition mostlyharmless_DatabaseState.h:222
Contains classes and functions related to data management.
Definition mostlyharmless_DatabaseState.h:16
std::variant< std::string, bool, int, float, double > DatabaseValueVariant
A std::variant containing all types satisfying the DatabaseStorageType concept.
Definition mostlyharmless_DatabaseState.h:54