diff --git a/CMakeLists.txt b/CMakeLists.txt index 4f153ec..1252706 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3,12 +3,15 @@ project(magic_scrolls) set(CMAKE_EXPORT_COMPILE_COMMANDS ON) +find_package(SQLite3 REQUIRED) + file(GLOB_RECURSE sources "${CMAKE_SOURCE_DIR}/src/*.cpp") add_executable(magic_scrolls ${sources}) target_include_directories(magic_scrolls PUBLIC "${CMAKE_SOURCE_DIR}/include") +target_link_libraries(magic_scrolls PRIVATE SQLite::SQLite3) # ------- enable_testing() @@ -17,10 +20,23 @@ add_executable(event_bus_test tests/engine/event_bus_test.cpp src/engine/core/event_bus.cpp ) +add_executable(db_provider_test + tests/engine/db_provider_test.cpp + src/engine/core/engine.cpp + src/engine/core/engine_db_provider.cpp +) target_include_directories(event_bus_test PUBLIC "${CMAKE_SOURCE_DIR}/include") +target_link_libraries(event_bus_test PRIVATE SQLite::SQLite3) + +target_include_directories(db_provider_test PUBLIC "${CMAKE_SOURCE_DIR}/include") +target_link_libraries(db_provider_test PRIVATE SQLite::SQLite3) add_test( NAME "Event Bus" COMMAND event_bus_test ) +add_test( + NAME "Database Provider" + COMMAND db_provider_test +) diff --git a/include/engine/core/engine.hpp b/include/engine/core/engine.hpp index bba9fae..e6b0284 100644 --- a/include/engine/core/engine.hpp +++ b/include/engine/core/engine.hpp @@ -1,8 +1,9 @@ #ifndef __MCS_ENGINE_HPP__ #define __MCS_ENGINE_HPP__ -#include "engine/core/engine_module.hpp" -#include "engine/core/engine_module_api.hpp" +#include +#include +#include #include #include #include @@ -24,6 +25,8 @@ public: MscEventBus &getBus() override; std::unordered_map getLoadedModules() const override; MscEngineModule &getModule(const std::string &name) override; + std::unique_ptr createDBProvider() override; + std::unique_ptr createDBProvider(const std::string &filepath) override; }; #endif // __MCS_ENGINE_HPP__ diff --git a/include/engine/core/engine_db_provider.hpp b/include/engine/core/engine_db_provider.hpp new file mode 100644 index 0000000..a3a936d --- /dev/null +++ b/include/engine/core/engine_db_provider.hpp @@ -0,0 +1,30 @@ +#ifndef __MSC_ENGINE_DB_PROVIDER_HPP__ +#define __MSC_ENGINE_DB_PROVIDER_HPP__ + +#include +#include + +/// @brief Engine database provider +class MscEngineDBProvider +{ +public: + ~MscEngineDBProvider(); + + /// @brief Create inmemory database + MscEngineDBProvider(); + + /// @brief Create file database + explicit MscEngineDBProvider(const std::string &filepath); + + /// @brief Execute SQL statement + /// @param sql statement + void execute(const std::string &sql); + + MscEngineDBProvider(const MscEngineDBProvider &) = delete; + MscEngineDBProvider &operator=(const MscEngineDBProvider &) = delete; + +private: + sqlite3 *_db = nullptr; +}; + +#endif // __MSC_ENGINE_DB_PROVIDER_HPP__ diff --git a/include/engine/core/engine_module_api.hpp b/include/engine/core/engine_module_api.hpp index f7adc7c..616bd9b 100644 --- a/include/engine/core/engine_module_api.hpp +++ b/include/engine/core/engine_module_api.hpp @@ -2,24 +2,33 @@ #define __MSC_ENGINE_MODULE_API_HPP__ #include -#include +#include #include #include #include +#include + +class MscEngineModule; class IMscEngineModuleAPI { public: virtual ~IMscEngineModuleAPI() = default; - /// Get access to engine event bus + /// @brief Get access to engine event bus virtual MscEventBus &getBus() = 0; - /// Get access to loaded module list + /// @brief Get access to loaded module list virtual std::unordered_map getLoadedModules() const = 0; - /// Access module by its name + /// @brief Access module by its name virtual MscEngineModule &getModule(const std::string &name) = 0; + + /// @brief Create database provider with in-memory database + virtual std::unique_ptr createDBProvider() = 0; + + /// @brief Create database provider with file database + virtual std::unique_ptr createDBProvider(const std::string &filepath) = 0; }; #endif // __MSC_ENGINE_MODULE_API_HPP__ diff --git a/src/engine/core/engine.cpp b/src/engine/core/engine.cpp index 011e8b4..95f8ff6 100644 --- a/src/engine/core/engine.cpp +++ b/src/engine/core/engine.cpp @@ -84,3 +84,13 @@ MscEngineModule &MscEngine::getModule(const std::string &name) throw std::runtime_error("Module not found: " + name); } + +std::unique_ptr MscEngine::createDBProvider() +{ + return std::make_unique(); +} + +std::unique_ptr MscEngine::createDBProvider(const std::string &filepath) +{ + return std::make_unique(filepath); +} diff --git a/src/engine/core/engine_db_provider.cpp b/src/engine/core/engine_db_provider.cpp new file mode 100644 index 0000000..e95d292 --- /dev/null +++ b/src/engine/core/engine_db_provider.cpp @@ -0,0 +1,45 @@ +#include +#include + +MscEngineDBProvider::MscEngineDBProvider() : MscEngineDBProvider(":memory:") +{ +} + +MscEngineDBProvider::MscEngineDBProvider(const std::string &filepath) +{ + const int result = sqlite3_open(filepath.c_str(), &_db); + if (result != SQLITE_OK) + { + const std::string message = _db != nullptr + ? sqlite3_errmsg(_db) + : "unknown SQLite error"; + + if (_db != nullptr) + { + sqlite3_close(_db); + _db = nullptr; + } + + throw std::runtime_error("Failed to open SQLite database: " + message); + } +} + +MscEngineDBProvider::~MscEngineDBProvider() +{ + if (_db != nullptr) + sqlite3_close(_db); +} + +void MscEngineDBProvider::execute(const std::string &sql) +{ + char *errorMessage = nullptr; + const int result = sqlite3_exec(_db, sql.c_str(), nullptr, nullptr, &errorMessage); + + if (result == SQLITE_OK) + return; + + std::string message(errorMessage); + sqlite3_free(errorMessage); + + throw std::runtime_error("Failed to execute SQL: " + message); +} diff --git a/tests/engine/db_provider_test.cpp b/tests/engine/db_provider_test.cpp new file mode 100644 index 0000000..c085da1 --- /dev/null +++ b/tests/engine/db_provider_test.cpp @@ -0,0 +1,62 @@ +#include +#include +#include +#include +#include + +/// @brief Проверка на создание экземпляра +void testProviderCreation() +{ + MscEngine engine({}); + + auto dbp1 = engine.createDBProvider(); + assert(dbp1.get() != nullptr); +} + +/// @brief Проверка на создание экземпляра с некорректным путём файла +void testIncorrectFilePath() +{ + MscEngine engine({}); + + try + { + engine.createDBProvider("/i/am/an/incorrect/path/to/file.txt"); + assert(false); + } + catch (const std::runtime_error &error) + { + } +} + +/// @brief Проверка на успешное выполнение корректного выражения +void testValidStatement() +{ + MscEngine engine({}); + auto dbp = engine.createDBProvider(); + dbp.get()->execute("SELECT 'hello, world' AS message;"); +} + +void testInvalidStatement() +{ + MscEngine engine({}); + auto dbp = engine.createDBProvider(); + + try + { + dbp.get()->execute("SELECT * FROM nonexistentdatabase ABC"); + assert(false); + } + catch (const std::runtime_error &error) + { + } +} + +int main() +{ + testProviderCreation(); + testIncorrectFilePath(); + testValidStatement(); + testInvalidStatement(); + + return 0; +}