diff --git a/cpp17/.clang-format b/cpp17/.clang-format new file mode 100644 index 0000000..1fce9b2 --- /dev/null +++ b/cpp17/.clang-format @@ -0,0 +1,83 @@ +--- +Language: Cpp +AccessModifierOffset: -2 +AlignAfterOpenBracket: Align +AlignConsecutiveAssignments: false +AlignConsecutiveDeclarations: false +AlignEscapedNewlines: Right +AlignOperands: true +AlignTrailingComments: true +AllowAllArgumentsOnNextLine: true +AllowAllConstructorInitializersOnNextLine: true +AllowAllParametersOfDeclarationOnNextLine: true +AllowShortBlocksOnASingleLine: false +AllowShortCaseLabelsOnASingleLine: false +AllowShortFunctionsOnASingleLine: Inline +AllowShortIfStatementsOnASingleLine: false +AllowShortLoopsOnASingleLine: false +AlwaysBreakAfterDefinitionReturnType: None +AlwaysBreakAfterReturnType: None +AlwaysBreakBeforeMultilineStrings: false +AlwaysBreakTemplateDeclarations: MultiLine +BinPackArguments: true +BinPackParameters: true +BreakBeforeBinaryOperators: NonAssignment +BreakBeforeBraces: Custom +BraceWrapping: + AfterClass: true + AfterControlStatement: false + AfterEnum: false + AfterFunction: true + AfterNamespace: false + AfterStruct: true + AfterUnion: false + AfterExternBlock: false + BeforeCatch: false + BeforeElse: false + IndentBraces: false + SplitEmptyFunction: false + SplitEmptyRecord: false + SplitEmptyNamespace: false +BreakBeforeInheritanceComma: false +BreakInheritanceList: BeforeColon +ColumnLimit: 80 +CompactNamespaces: false +ConstructorInitializerIndentWidth: 2 +ContinuationIndentWidth: 2 +Cpp11BracedListStyle: true +DerivePointerAlignment: false +DisableFormat: false +ExperimentalAutoDetectBinPacking: false +FixNamespaceComments: true +IndentCaseLabels: true +IndentPPDirectives: None +IndentWidth: 2 +IndentWrappedFunctionNames: false +KeepEmptyLinesAtTheStartOfBlocks: false +# LambdaBodyIndentation: Signature +MaxEmptyLinesToKeep: 1 +NamespaceIndentation: None +PointerAlignment: Left +ReflowComments: true +SortIncludes: false +SortUsingDeclarations: false +SpaceAfterCStyleCast: false +SpaceAfterLogicalNot: false +SpaceAfterTemplateKeyword: true +# SpaceAroundPointerQualifiers: Default +SpaceBeforeAssignmentOperators: true +SpaceBeforeCpp11BracedList: false +SpaceBeforeCtorInitializerColon: true +SpaceBeforeInheritanceColon: true +SpaceBeforeParens: ControlStatements +SpaceBeforeRangeBasedForLoopColon: true +SpaceInEmptyParentheses: false +SpacesBeforeTrailingComments: 1 +SpacesInAngles: false +SpacesInCStyleCastParentheses: false +SpacesInConditionalStatement: false +SpacesInContainerLiterals: true +SpacesInParentheses: false +SpacesInSquareBrackets: false +TabWidth: 8 +UseTab: Never diff --git a/cpp17/app/src/App.cpp b/cpp17/app/src/App.cpp index 333c014..289b258 100644 --- a/cpp17/app/src/App.cpp +++ b/cpp17/app/src/App.cpp @@ -1,25 +1,37 @@ #include "App.h" -#include "autostore/AutoStore.h" #include -namespace nxl +namespace nxl { + +std::condition_variable App::exitCv; +std::mutex App::mtx; +bool App::shouldExit = false; + +App::App(int argc, char** argv) { - App::App(int argc, char **argv) - { - signal(SIGINT, App::handle_signal); - signal(SIGTERM, App::handle_signal); - } - - int App::exec() - { - nxl::AutoStore autostore; - autostore.run(); - return 0; - } - - void App::handle_signal(int signum) - { - std::cout << "\nCaught signal " << signum << ". Graceful shutdown." << std::endl; - exit(signum); - } -} \ No newline at end of file + signal(SIGINT, App::handleSignal); + signal(SIGTERM, App::handleSignal); +} + +int App::exec() +{ + // TODO: start application services when implemented + + std::unique_lock lock(mtx); + exitCv.wait(lock, [] { return shouldExit; }); + + return 0; +} + +void App::handleSignal(int signum) +{ + std::cout << "\nCaught signal " << signum << ". Graceful shutdown." + << std::endl; + { + std::lock_guard lock(mtx); + shouldExit = true; + } + exitCv.notify_one(); +} + +} // namespace nxl \ No newline at end of file diff --git a/cpp17/app/src/App.h b/cpp17/app/src/App.h index 39b048b..29fab1d 100644 --- a/cpp17/app/src/App.h +++ b/cpp17/app/src/App.h @@ -1,16 +1,24 @@ #pragma once +#include +#include #include +#include +#include -namespace nxl +namespace nxl { + +class App { - class App - { - public: - App(int argc, char **argv); - int exec(); +public: + App(int argc, char** argv); + int exec(); + +private: + static void handleSignal(int signum); + static std::condition_variable exitCv; + static std::mutex mtx; + static bool shouldExit; +}; - private: - static void handle_signal(int signum); - }; -} \ No newline at end of file +} // namespace nxl \ No newline at end of file diff --git a/cpp17/app/src/Main.cpp b/cpp17/app/src/Main.cpp index 07d46d7..a9baa97 100644 --- a/cpp17/app/src/Main.cpp +++ b/cpp17/app/src/Main.cpp @@ -2,9 +2,9 @@ #include "Version.h" #include -int main(int argc, char **argv) +int main(int argc, char** argv) { - std::cout << "AutoStore v" << nxl::getVersionString() << std::endl; - nxl::App app(argc, argv); - return app.exec(); + std::cout << "AutoStore v" << nxl::getVersionString() << std::endl; + nxl::App app(argc, argv); + return app.exec(); } diff --git a/cpp17/app/src/Version.h.in b/cpp17/app/src/Version.h.in index 4eb6c96..b92fe98 100644 --- a/cpp17/app/src/Version.h.in +++ b/cpp17/app/src/Version.h.in @@ -3,17 +3,18 @@ #include -namespace nxl -{ - static constexpr int VERSION_MAJOR = ${PROJECT_VERSION_MAJOR}; - static constexpr int VERSION_MINOR = ${PROJECT_VERSION_MINOR}; - static constexpr int VERSION_PATCH = ${PROJECT_VERSION_PATCH}; - static constexpr char VERSION_SUFFIX[] = "${PROJECT_VERSION_SUFFIX}"; +namespace nxl { + +static constexpr int VERSION_MAJOR = ${PROJECT_VERSION_MAJOR}; +static constexpr int VERSION_MINOR = ${PROJECT_VERSION_MINOR}; +static constexpr int VERSION_PATCH = ${PROJECT_VERSION_PATCH}; +static constexpr char VERSION_SUFFIX[] = "${PROJECT_VERSION_SUFFIX}"; - inline std::string getVersionString() - { - return std::to_string(VERSION_MAJOR) + "." + std::to_string(VERSION_MINOR) + "." + std::to_string(VERSION_PATCH) + VERSION_SUFFIX; - } +inline std::string getVersionString() +{ + return std::to_string(VERSION_MAJOR) + "." + std::to_string(VERSION_MINOR) + + "." + std::to_string(VERSION_PATCH) + VERSION_SUFFIX; +} } // namespace nxl diff --git a/cpp17/lib/CMakeLists.txt b/cpp17/lib/CMakeLists.txt index f687ee9..0a9b243 100644 --- a/cpp17/lib/CMakeLists.txt +++ b/cpp17/lib/CMakeLists.txt @@ -7,20 +7,35 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON) add_library(${TARGET_NAME} STATIC - src/AutoStore.cpp + src/infrastructure/repositories/FileUserRepository.cpp + src/infrastructure/repositories/FileItemRepository.cpp ) target_include_directories(${TARGET_NAME} PUBLIC - ${CMAKE_CURRENT_SOURCE_DIR}/include + ${CMAKE_CURRENT_SOURCE_DIR}/include/autostore +) + +target_sources(${TARGET_NAME} + PUBLIC + FILE_SET HEADERS + BASE_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/include + FILES + include/autostore/domain/entities/User.h + include/autostore/domain/entities/Item.h + include/autostore/application/interfaces/IUserRepository.h + include/autostore/application/interfaces/IItemRepository.h + include/autostore/application/interfaces/IAuthService.h ) # Find dependencies find_package(httplib CONFIG REQUIRED) find_package(Catch2 CONFIG REQUIRED) +find_package(nlohmann_json CONFIG REQUIRED) target_link_libraries(${TARGET_NAME} PUBLIC httplib::httplib Catch2::Catch2WithMain + nlohmann_json::nlohmann_json ) \ No newline at end of file diff --git a/cpp17/lib/include/autostore/AutoStore.h b/cpp17/lib/include/autostore/AutoStore.h deleted file mode 100644 index 0338ad9..0000000 --- a/cpp17/lib/include/autostore/AutoStore.h +++ /dev/null @@ -1,11 +0,0 @@ -#pragma once - -namespace nxl -{ - class AutoStore - { - public: - AutoStore(); - void run(); - }; -} // namespace nxl diff --git a/cpp17/lib/include/autostore/application/interfaces/IAuthService.h b/cpp17/lib/include/autostore/application/interfaces/IAuthService.h new file mode 100644 index 0000000..cd74605 --- /dev/null +++ b/cpp17/lib/include/autostore/application/interfaces/IAuthService.h @@ -0,0 +1,21 @@ +#pragma once + +#include +#include +#include + +namespace nxl { +namespace autostore { +namespace application { + +class IAuthService +{ +public: + virtual ~IAuthService() = default; + virtual std::string generateToken(std::string_view userId) = 0; + virtual std::optional validateToken(std::string_view token) = 0; +}; + +} // namespace application +} // namespace autostore +} // namespace nxl \ No newline at end of file diff --git a/cpp17/lib/include/autostore/application/interfaces/IItemRepository.h b/cpp17/lib/include/autostore/application/interfaces/IItemRepository.h new file mode 100644 index 0000000..a0d0efd --- /dev/null +++ b/cpp17/lib/include/autostore/application/interfaces/IItemRepository.h @@ -0,0 +1,26 @@ +#pragma once + +#include "domain/entities/Item.h" +#include +#include +#include +#include + +namespace nxl { +namespace autostore { +namespace application { + +class IItemRepository +{ +public: + virtual ~IItemRepository() = default; + virtual void save(const domain::Item& item) = 0; + virtual std::optional findById(std::string_view id) = 0; + virtual std::vector findByUser(std::string_view userId) = 0; + virtual std::vector findAll() = 0; + virtual void remove(std::string_view id) = 0; +}; + +} // namespace application +} // namespace autostore +} // namespace nxl \ No newline at end of file diff --git a/cpp17/lib/include/autostore/application/interfaces/IUserRepository.h b/cpp17/lib/include/autostore/application/interfaces/IUserRepository.h new file mode 100644 index 0000000..59641c8 --- /dev/null +++ b/cpp17/lib/include/autostore/application/interfaces/IUserRepository.h @@ -0,0 +1,27 @@ +#pragma once + +#include "domain/entities/User.h" +#include +#include +#include +#include + +namespace nxl { +namespace autostore { +namespace application { + +class IUserRepository +{ +public: + virtual ~IUserRepository() = default; + virtual void save(const domain::User& user) = 0; + virtual std::optional findById(std::string_view id) = 0; + virtual std::optional + findByUsername(std::string_view username) = 0; + virtual std::vector findAll() = 0; + virtual void remove(std::string_view id) = 0; +}; + +} // namespace application +} // namespace autostore +} // namespace nxl \ No newline at end of file diff --git a/cpp17/lib/include/autostore/domain/entities/Item.h b/cpp17/lib/include/autostore/domain/entities/Item.h new file mode 100644 index 0000000..04d1e35 --- /dev/null +++ b/cpp17/lib/include/autostore/domain/entities/Item.h @@ -0,0 +1,21 @@ +#pragma once + +#include +#include + +namespace nxl { +namespace autostore { +namespace domain { + +struct Item +{ + std::string id; + std::string name; + std::chrono::system_clock::time_point expirationDate; + std::string orderUrl; + std::string userId; +}; + +} // namespace domain +} // namespace autostore +} // namespace nxl \ No newline at end of file diff --git a/cpp17/lib/include/autostore/domain/entities/User.h b/cpp17/lib/include/autostore/domain/entities/User.h new file mode 100644 index 0000000..f28f1d2 --- /dev/null +++ b/cpp17/lib/include/autostore/domain/entities/User.h @@ -0,0 +1,18 @@ +#pragma once + +#include + +namespace nxl { +namespace autostore { +namespace domain { + +struct User +{ + std::string id; + std::string username; + std::string passwordHash; +}; + +} // namespace domain +} // namespace autostore +} // namespace nxl \ No newline at end of file diff --git a/cpp17/lib/include/autostore/infrastructure/repositories/FileItemRepository.h b/cpp17/lib/include/autostore/infrastructure/repositories/FileItemRepository.h new file mode 100644 index 0000000..4be0415 --- /dev/null +++ b/cpp17/lib/include/autostore/infrastructure/repositories/FileItemRepository.h @@ -0,0 +1,33 @@ +#pragma once + +#include "application/interfaces/IItemRepository.h" +#include +#include +#include + +namespace nxl { +namespace autostore { +namespace infrastructure { + +class FileItemRepository : public application::IItemRepository +{ +public: + explicit FileItemRepository(std::string_view dbPath); + void save(const domain::Item& item) override; + std::optional findById(std::string_view id) override; + std::vector findByUser(std::string_view userId) override; + std::vector findAll() override; + void remove(std::string_view id) override; + +private: + void load(); + void persist(); + + std::string dbPath; + std::vector items; + std::mutex mtx; +}; + +} // namespace infrastructure +} // namespace autostore +} // namespace nxl \ No newline at end of file diff --git a/cpp17/lib/include/autostore/infrastructure/repositories/FileUserRepository.h b/cpp17/lib/include/autostore/infrastructure/repositories/FileUserRepository.h new file mode 100644 index 0000000..8594ff8 --- /dev/null +++ b/cpp17/lib/include/autostore/infrastructure/repositories/FileUserRepository.h @@ -0,0 +1,34 @@ +#pragma once + +#include "application/interfaces/IUserRepository.h" +#include +#include +#include + +namespace nxl { +namespace autostore { +namespace infrastructure { + +class FileUserRepository : public application::IUserRepository +{ +public: + explicit FileUserRepository(std::string_view dbPath); + void save(const domain::User& user) override; + std::optional findById(std::string_view id) override; + std::optional + findByUsername(std::string_view username) override; + std::vector findAll() override; + void remove(std::string_view id) override; + +private: + void load(); + void persist(); + + std::string dbPath; + std::vector users; + std::mutex mtx; +}; + +} // namespace infrastructure +} // namespace autostore +} // namespace nxl \ No newline at end of file diff --git a/cpp17/lib/src/AutoStore.cpp b/cpp17/lib/src/AutoStore.cpp deleted file mode 100644 index 8e7f14b..0000000 --- a/cpp17/lib/src/AutoStore.cpp +++ /dev/null @@ -1,15 +0,0 @@ -#include "autostore/AutoStore.h" -#include - -namespace nxl -{ - AutoStore::AutoStore() - { - std::cout << "AutoStore library initialized." << std::endl; - } - - void AutoStore::run() - { - std::cout << "AutoStore library is running." << std::endl; - } -} // namespace nxl \ No newline at end of file diff --git a/cpp17/lib/src/infrastructure/repositories/FileItemRepository.cpp b/cpp17/lib/src/infrastructure/repositories/FileItemRepository.cpp new file mode 100644 index 0000000..cde7a8f --- /dev/null +++ b/cpp17/lib/src/infrastructure/repositories/FileItemRepository.cpp @@ -0,0 +1,142 @@ +#include "infrastructure/repositories/FileItemRepository.h" +#include "nlohmann/json.hpp" +#include +#include +#include +#include +#include + +namespace nxl { +namespace autostore { +namespace infrastructure { + +namespace { + +// Helper functions for JSON serialization +inline void itemToJson(nlohmann::json& j, const domain::Item& item) +{ + j = + nlohmann::json{{"id", item.id}, + {"name", item.name}, + {"expirationDate", + std::chrono::system_clock::to_time_t(item.expirationDate)}, + {"orderUrl", item.orderUrl}, + {"userId", item.userId}}; +} + +inline void jsonToItem(const nlohmann::json& j, domain::Item& item) +{ + j.at("id").get_to(item.id); + j.at("name").get_to(item.name); + std::time_t expirationTime; + j.at("expirationDate").get_to(expirationTime); + item.expirationDate = std::chrono::system_clock::from_time_t(expirationTime); + j.at("orderUrl").get_to(item.orderUrl); + j.at("userId").get_to(item.userId); +} + +// Helper functions for vector serialization +inline nlohmann::json itemsToJson(const std::vector& items) +{ + nlohmann::json j = nlohmann::json::array(); + for (const auto& item : items) { + nlohmann::json itemJson; + itemToJson(itemJson, item); + j.push_back(itemJson); + } + return j; +} + +inline std::vector jsonToItems(const nlohmann::json& j) +{ + std::vector items; + for (const auto& itemJson : j) { + domain::Item item; + jsonToItem(itemJson, item); + items.push_back(item); + } + return items; +} + +} // namespace + +FileItemRepository::FileItemRepository(std::string_view dbPath) : dbPath(dbPath) +{ + load(); +} + +void FileItemRepository::save(const domain::Item& item) +{ + std::lock_guard lock(mtx); + auto it = + std::find_if(items.begin(), items.end(), + [&](const domain::Item& i) { return i.id == item.id; }); + + if (it != items.end()) { + *it = item; + } else { + items.push_back(item); + } + persist(); +} + +std::optional FileItemRepository::findById(std::string_view id) +{ + std::lock_guard lock(mtx); + auto it = std::find_if(items.begin(), items.end(), + [&](const domain::Item& i) { return i.id == id; }); + + if (it != items.end()) { + return *it; + } + return std::nullopt; +} + +std::vector +FileItemRepository::findByUser(std::string_view userId) +{ + std::lock_guard lock(mtx); + std::vector userItems; + std::copy_if(items.begin(), items.end(), std::back_inserter(userItems), + [&](const domain::Item& i) { return i.userId == userId; }); + return userItems; +} + +std::vector FileItemRepository::findAll() +{ + std::lock_guard lock(mtx); + return items; +} + +void FileItemRepository::remove(std::string_view id) +{ + std::lock_guard lock(mtx); + items.erase(std::remove_if(items.begin(), items.end(), + [&](const domain::Item& i) { return i.id == id; }), + items.end()); + persist(); +} + +void FileItemRepository::load() +{ + std::lock_guard lock(mtx); + std::ifstream file(dbPath); + if (file.is_open()) { + nlohmann::json j; + file >> j; + items = jsonToItems(j); + } +} + +void FileItemRepository::persist() +{ + std::ofstream file(dbPath); + if (file.is_open()) { + nlohmann::json j = itemsToJson(items); + file << j.dump(4); + } +} + +} // namespace infrastructure +} // namespace autostore +} // namespace nxl \ No newline at end of file diff --git a/cpp17/lib/src/infrastructure/repositories/FileUserRepository.cpp b/cpp17/lib/src/infrastructure/repositories/FileUserRepository.cpp new file mode 100644 index 0000000..0073699 --- /dev/null +++ b/cpp17/lib/src/infrastructure/repositories/FileUserRepository.cpp @@ -0,0 +1,134 @@ +#include "infrastructure/repositories/FileUserRepository.h" +#include "nlohmann/json.hpp" +#include +#include + +namespace nxl { +namespace autostore { +namespace infrastructure { + +namespace { + +// Helper functions for JSON serialization +inline void userToJson(nlohmann::json& j, const domain::User& u) +{ + j = nlohmann::json{ + {"id", u.id}, {"username", u.username}, {"passwordHash", u.passwordHash}}; +} + +inline void jsonToUser(const nlohmann::json& j, domain::User& u) +{ + j.at("id").get_to(u.id); + j.at("username").get_to(u.username); + j.at("passwordHash").get_to(u.passwordHash); +} + +// Helper functions for vector serialization +inline nlohmann::json usersToJson(const std::vector& users) +{ + nlohmann::json j = nlohmann::json::array(); + for (const auto& user : users) { + nlohmann::json userJson; + userToJson(userJson, user); + j.push_back(userJson); + } + return j; +} + +inline std::vector jsonToUsers(const nlohmann::json& j) +{ + std::vector users; + for (const auto& userJson : j) { + domain::User user; + jsonToUser(userJson, user); + users.push_back(user); + } + return users; +} + +} // namespace + +FileUserRepository::FileUserRepository(std::string_view dbPath) : dbPath(dbPath) +{ + load(); +} + +void FileUserRepository::save(const domain::User& user) +{ + std::lock_guard lock(mtx); + auto it = + std::find_if(users.begin(), users.end(), + [&](const domain::User& u) { return u.id == user.id; }); + + if (it != users.end()) { + *it = user; + } else { + users.push_back(user); + } + persist(); +} + +std::optional FileUserRepository::findById(std::string_view id) +{ + std::lock_guard lock(mtx); + auto it = std::find_if(users.begin(), users.end(), + [&](const domain::User& u) { return u.id == id; }); + + if (it != users.end()) { + return *it; + } + return std::nullopt; +} + +std::optional +FileUserRepository::findByUsername(std::string_view username) +{ + std::lock_guard lock(mtx); + auto it = + std::find_if(users.begin(), users.end(), + [&](const domain::User& u) { return u.username == username; }); + + if (it != users.end()) { + return *it; + } + return std::nullopt; +} + +std::vector FileUserRepository::findAll() +{ + std::lock_guard lock(mtx); + return users; +} + +void FileUserRepository::remove(std::string_view id) +{ + std::lock_guard lock(mtx); + users.erase(std::remove_if(users.begin(), users.end(), + [&](const domain::User& u) { return u.id == id; }), + users.end()); + persist(); +} + +void FileUserRepository::load() +{ + std::lock_guard lock(mtx); + std::ifstream file(dbPath); + if (file.is_open()) { + nlohmann::json j; + file >> j; + users = jsonToUsers(j); + } +} + +void FileUserRepository::persist() +{ + std::ofstream file(dbPath); + if (file.is_open()) { + nlohmann::json j = usersToJson(users); + file << j.dump(4); + } +} + +} // namespace infrastructure +} // namespace autostore +} // namespace nxl \ No newline at end of file diff --git a/cpp17/vcpkg.json b/cpp17/vcpkg.json index 4b8aba5..cecd049 100644 --- a/cpp17/vcpkg.json +++ b/cpp17/vcpkg.json @@ -3,6 +3,7 @@ "version-string": "1.0.0", "dependencies": [ "cpp-httplib", - "catch2" + "catch2", + "nlohmann-json" ] }