diff --git a/backend/include/database.h b/backend/include/database.h index e1ca2aa..08107c6 100644 --- a/backend/include/database.h +++ b/backend/include/database.h @@ -19,11 +19,16 @@ class UserDataBase { class BookDataBase { DriveArray full_book_data; String2Index indexer; - String2Index keyword2index; + + public: + void Open(std::string file_name); }; class LogDataBase { DriveArray finance_data; DriveArray operation_log_data; + + public: + void Open(std::string file_name); }; #endif // PROTECTOR_DATABASE_HPP \ No newline at end of file diff --git a/backend/include/engine.h b/backend/include/engine.h index d9f4f2e..e96b284 100644 --- a/backend/include/engine.h +++ b/backend/include/engine.h @@ -9,11 +9,42 @@ class BookStoreEngineClass { std::string config_dir; UserDataBase user_data_base; + BookDataBase book_data_base; + LogDataBase log_data_base; bool is_server; + public: BookStoreEngineClass() = delete; BookStoreEngineClass(std::string __config_dir, bool __is_server); std::vector Execute(const std::string &cmd, - std::stack &login_stack); + std::stack> &login_stack); + std::vector ExecuteSu(const std::string &cmd, + std::stack> &login_stack); + std::vector ExecuteLogout(const std::string &cmd, + std::stack> &login_stack); + std::vector ExecuteRegister( + const std::string &cmd, std::stack> &login_stack); + std::vector ExecutePasswd(const std::string &cmd, + std::stack> &login_stack); + std::vector ExecuteUserAdd(const std::string &cmd, + std::stack> &login_stack); + std::vector ExecuteDelete(const std::string &cmd, + std::stack> &login_stack); + std::vector ExecuteShow(const std::string &cmd, + std::stack> &login_stack); + std::vector ExecuteBuy(const std::string &cmd, + std::stack> &login_stack); + std::vector ExecuteSelect(const std::string &cmd, + std::stack> &login_stack); + std::vector ExecuteMOdify(const std::string &cmd, + std::stack> &login_stack); + std::vector ExecuteImport(const std::string &cmd, + std::stack> &login_stack); + std::vector ExecuteShowFinance( + const std::string &cmd, std::stack> &login_stack); + std::vector ExecuteLog(const std::string &cmd, + std::stack> &login_stack); + std::vector ExecuteReport(const std::string &cmd, + std::stack> &login_stack); }; #endif // PROTECTOR_ENGINE_H \ No newline at end of file diff --git a/backend/include/lexer.h b/backend/include/lexer.h index 81ec949..235884f 100644 --- a/backend/include/lexer.h +++ b/backend/include/lexer.h @@ -4,7 +4,27 @@ #include #include #include +bool CommandSuLexer(const std::string &command, std::string &user_id, + std::string &password); +bool CommandLogoutLexer(const std::string &command); +bool CommandRegisterLexer(const std::string &command, std::string &user_id, + std::string &password, std::string &username); +bool CommandPasswdLexer(const std::string &command, std::string &user_id, + std::string &old_password, std::string &new_password); +bool CommandUseraddLexer(const std::string &command, std::string &user_id, + std::string &password, int &privilege, + std::string &username); +bool CommandDeleteLexer(const std::string &command, std::string &user_id); bool CommandShowLexer(const std::string &command, std::string &ISBN, std::string &name, std::string &author, std::string &keyword); +bool CommandBuyLexer(const std::string &command, std::string &ISBN, + int &quantity); +bool CommandSelectLexer(const std::string &command, std::string &ISBN); +bool CommandModifyLexer(const std::string &command, std::string &ISBN, + std::string &name, std::string &author, + std::string &keyword, double &price); +bool CommandImportLexer(const std::string &command, int &quantity, + double &total_cost); +bool CommandShowfinanceLexer(const std::string &command,int &count); #endif // PROTECTOR_LEXER_H \ No newline at end of file diff --git a/backend/src/builtin-cli.cpp b/backend/src/builtin-cli.cpp index a681f7c..e2e5281 100644 --- a/backend/src/builtin-cli.cpp +++ b/backend/src/builtin-cli.cpp @@ -5,6 +5,7 @@ #include #include #include +#include #include "bs-utility.h" #include "engine.h" @@ -12,7 +13,7 @@ void BookStoreMain(bool is_server, std::string config_dir) { BookStoreEngineClass engine(config_dir, is_server); std::ios::sync_with_stdio(false); if (!is_server) { - std::stack login_stack; + std::stack> login_stack; std::string cmd; while (std::getline(std::cin, cmd)) { auto result = std::move(engine.Execute(cmd, login_stack)); diff --git a/backend/src/database.cpp b/backend/src/database.cpp index 1a8d0d8..fd2bfe3 100644 --- a/backend/src/database.cpp +++ b/backend/src/database.cpp @@ -3,4 +3,14 @@ void UserDataBase::Open(std::string file_name) { full_user_data.OpenFile(file_name + ".full"); user_name2index.OpenFile(file_name + ".n2i"); +} + +void BookDataBase::Open(std::string file_name) { + full_book_data.OpenFile(file_name + ".full"); + indexer.OpenFile(file_name + ".n2i"); +} + +void LogDataBase::Open(std::string file_name) { + finance_data.OpenFile(file_name + ".finance"); + operation_log_data.OpenFile(file_name + ".log"); } \ No newline at end of file diff --git a/backend/src/engine.cpp b/backend/src/engine.cpp index 8e14942..f5c41bf 100644 --- a/backend/src/engine.cpp +++ b/backend/src/engine.cpp @@ -1,5 +1,6 @@ #include "engine.h" +#include #include #include #include @@ -10,10 +11,15 @@ BookStoreEngineClass::BookStoreEngineClass(std::string __config_dir, bool __is_server) { config_dir = __config_dir; user_data_base.Open(config_dir + "user"); + book_data_base.Open(config_dir + "book"); + log_data_base.Open(config_dir + "log"); is_server = __is_server; } std::vector BookStoreEngineClass::Execute( - const std::string &cmd, std::stack &login_stack) { + const std::string &cmd, std::stack> &login_stack) { + for (int i = 0; i < cmd.length(); i++) + if (std::isspace(cmd[i]) && cmd[i] != ' ') + return std::vector({"Invalid"}); static std::unordered_map operation_map = { {"su", OperationType::__Ksu}, {"logout", OperationType::__Klogout}, @@ -42,38 +48,123 @@ std::vector BookStoreEngineClass::Execute( } switch (operation_map[head]) { case OperationType::__Ksu: { + return ExecuteSu(cmd, login_stack); } case OperationType::__Klogout: { + return ExecuteLogout(cmd, login_stack); } case OperationType::__Kuseradd: { + return ExecuteUserAdd(cmd, login_stack); } case OperationType::__Kregister: { + return ExecuteRegister(cmd, login_stack); } case OperationType::__Kdelete: { + return ExecuteDelete(cmd, login_stack); } case OperationType::__Kpasswd: { + return ExecutePasswd(cmd, login_stack); } case OperationType::__Kselect: { + return ExecuteSelect(cmd, login_stack); } case OperationType::__Kmodify: { + return ExecuteMOdify(cmd, login_stack); } case OperationType::__Kimport: { + return ExecuteImport(cmd, login_stack); } case OperationType::__Kshow: { ss >> head; if (head == "finance") goto dst_showfinance; + return ExecuteShow(cmd, login_stack); } case OperationType::__Kshowfinance: { dst_showfinance:; + return ExecuteShowFinance(cmd, login_stack); } case OperationType::__Kbuy: { + return ExecuteBuy(cmd, login_stack); } case OperationType::__Kreport: { - throw FatalError("report Not implemented", 2); + // throw FatalError("report Not implemented", 2); + return ExecuteReport(cmd, login_stack); } case OperationType::__Klog: { - throw FatalError("log Not implemented", 3); + // throw FatalError("log Not implemented", 3); + return ExecuteLog(cmd, login_stack); } } + throw FatalError("Unknown Command", 5); return std::vector({cmd}); +} + +std::vector BookStoreEngineClass::ExecuteSu( + const std::string &cmd, std::stack> &login_stack) { + return std::vector(); +} + +std::vector BookStoreEngineClass::ExecuteLogout( + const std::string &cmd, std::stack> &login_stack) { + return std::vector(); +} + +std::vector BookStoreEngineClass::ExecuteRegister( + const std::string &cmd, std::stack> &login_stack) { + return std::vector(); +} + +std::vector BookStoreEngineClass::ExecutePasswd( + const std::string &cmd, std::stack> &login_stack) { + return std::vector(); +} + +std::vector BookStoreEngineClass::ExecuteUserAdd( + const std::string &cmd, std::stack> &login_stack) { + return std::vector(); +} + +std::vector BookStoreEngineClass::ExecuteDelete( + const std::string &cmd, std::stack> &login_stack) { + return std::vector(); +} + +std::vector BookStoreEngineClass::ExecuteShow( + const std::string &cmd, std::stack> &login_stack) { + return std::vector(); +} + +std::vector BookStoreEngineClass::ExecuteBuy( + const std::string &cmd, std::stack> &login_stack) { + return std::vector(); +} + +std::vector BookStoreEngineClass::ExecuteSelect( + const std::string &cmd, std::stack> &login_stack) { + return std::vector(); +} + +std::vector BookStoreEngineClass::ExecuteMOdify( + const std::string &cmd, std::stack> &login_stack) { + return std::vector(); +} + +std::vector BookStoreEngineClass::ExecuteImport( + const std::string &cmd, std::stack> &login_stack) { + return std::vector(); +} + +std::vector BookStoreEngineClass::ExecuteShowFinance( + const std::string &cmd, std::stack> &login_stack) { + return std::vector(); +} + +std::vector BookStoreEngineClass::ExecuteLog( + const std::string &cmd, std::stack> &login_stack) { + return std::vector(); +} + +std::vector BookStoreEngineClass::ExecuteReport( + const std::string &cmd, std::stack> &login_stack) { + return std::vector(); } \ No newline at end of file diff --git a/backend/src/lexer.cpp b/backend/src/lexer.cpp index dee0b79..1af67f4 100644 --- a/backend/src/lexer.cpp +++ b/backend/src/lexer.cpp @@ -1,4 +1,210 @@ #include "lexer.h" + +// clang-format off +/** + * @brief Lexer for command "su" + * + * @param command The command to be parsed + * @param user_id The user_id to be logged in + * @param password The password to be logged in + * + * @return true if the command is valid + * + * @note The command is valid if and only if it follows the following grammar: + * su [UserID] ([Password])? +*/ +// clang-format on +bool CommandSuLexer(const std::string &command, std::string &user_id, + std::string &password) { + static std::basic_regex main_pattern( + R"(^ *su +(?:[0-9a-zA-Z_]{1,30}) +(?:[0-9a-zA-Z_]{1,30})? *$)", + std::regex_constants::optimize); + if (std::regex_match(command, main_pattern)) { + std::stringstream ss(command); + std::string token; + ss >> token; + user_id = ""; + password = ""; + ss >> user_id; + ss >> password; + return true; + } else + return false; +} + +// clang-format off +/** + * @brief Lexer for command "logout" + * + * @param command The command to be parsed + * + * @return true if the command is valid + * + * @note The command is valid if and only if it follows the following grammar: + * logout +*/ +// clang-format on +bool CommandLogoutLexer(const std::string &command) { + static std::basic_regex main_pattern(R"(^ *logout *$)", + std::regex_constants::optimize); + return std::regex_match(command, main_pattern); +} + +// clang-format off +/** + * @brief Lexer for command "register" + * + * @param command The command to be parsed + * @param user_id The user_id to be registered + * @param password The password to be registered + * + * @return true if the command is valid + * + * @note The command is valid if and only if it follows the following grammar: + * register [UserID] [Password] [Username] +*/ +// clang-format on +bool CommandRegisterLexer(const std::string &command, std::string &user_id, + std::string &password, std::string &username) { + static std::basic_regex main_pattern( + R"(^ *register +(?:[0-9a-zA-Z_]{1,30}) +(?:[0-9a-zA-Z_]{1,30}) +(?:[^\s]{1,30}) *$)", + std::regex_constants::optimize); + if (std::regex_match(command, main_pattern)) { + std::stringstream ss(command); + std::string token; + ss >> token; + user_id = ""; + password = ""; + username = ""; + ss >> user_id; + ss >> password; + ss >> username; + return true; + } else + return false; +} + +// clang-format off +/** + * @brief Lexer for command "passwd" + * + * @param command The command to be parsed + * @param user_id The user_id to be changed password + * @param old_password The old password + * @param new_password The new password + * + * @return true if the command is valid + * + * @note The command is valid if and only if it follows the following grammar: + * passwd [UserID] ([CurrentPassword])? [NewPassword] +*/ +// clang-format on +bool CommandPasswdLexer(const std::string &command, std::string &user_id, + std::string &old_password, std::string &new_password) { + static std::basic_regex main_pattern( + R"(^ *passwd +(?:[0-9a-zA-Z_]{1,30}) +(?:[0-9a-zA-Z_]{1,30})? +(?:[0-9a-zA-Z_]{1,30}) *$)", + std::regex_constants::optimize); + if (std::regex_match(command, main_pattern)) { + std::stringstream ss(command); + std::string token; + ss >> token; + user_id = ""; + old_password = ""; + new_password = ""; + ss >> user_id; + ss >> old_password; + ss >> new_password; + if (new_password == "") { + new_password = old_password; + old_password = ""; + } + return true; + } else + return false; +} + +// clang-format off +/** + * @brief Lexer for command "useradd" + * + * @param command The command to be parsed + * @param user_id The user_id to be added + * @param password The password to be added + * @param privilege The privilege to be added + * + * @return true if the command is valid + * + * @note The command is valid if and only if it follows the following grammar: + * useradd [UserID] [Password] [Privilege] [Username] +*/ +// clang-format on +bool CommandUseraddLexer(const std::string &command, std::string &user_id, + std::string &password, int &privilege, + std::string &username) { + static std::basic_regex main_pattern( + R"(^ *useradd +(?:[0-9a-zA-Z_]{1,30}) +(?:[0-9a-zA-Z_]{1,30}) +[0-9] +(?:[^\s]{1,30}) *$)", + std::regex_constants::optimize); + if (std::regex_match(command, main_pattern)) { + std::stringstream ss(command); + std::string token; + ss >> token; + user_id = ""; + password = ""; + privilege = 0; + username = ""; + ss >> user_id; + ss >> password; + ss >> privilege; + ss >> username; + return true; + } else + return false; +} + +// clang-format off +/** + * @brief Lexer for command "delete" + * + * @param command The command to be parsed + * @param user_id The user_id to be deleted + * + * @return true if the command is valid + * + * @note The command is valid if and only if it follows the following grammar: + * delete [UserID] +*/ +// clang-format on +bool CommandDeleteLexer(const std::string &command, std::string &user_id) { + static std::basic_regex main_pattern( + R"(^ *delete +(?:[0-9a-zA-Z_]{1,30}) *$)", + std::regex_constants::optimize); + if (std::regex_match(command, main_pattern)) { + std::stringstream ss(command); + std::string token; + ss >> token; + user_id = ""; + ss >> user_id; + return true; + } else + return false; +} + +// clang-format off +/** + * @brief Lexer for command "show" + * + * @param command The command to be parsed + * @param ISBN The ISBN of the book to be shown + * @param name The name of the book to be shown + * @param author The author of the book to be shown + * @param keyword The keyword of the book to be shown + * + * @return true if the command is valid + * + * @note The command is valid if and only if it follows the following grammar: + * show (-ISBN=[ISBN] | -name="[BookName]" | -author="[Author]" | -keyword="[Keyword]")? +*/ +// clang-format on bool CommandShowLexer(const std::string &command, std::string &ISBN, std::string &name, std::string &author, std::string &keyword) { @@ -6,7 +212,6 @@ bool CommandShowLexer(const std::string &command, std::string &ISBN, R"(^ *show(?: +-ISBN=(?:\S{1,20})| +-name=\"(?:[^\s"]{1,60})\"| +-author=\"(?:[^\s"]{1,60})\"| +-keyword=\"(?:[^\s"]{1,60})\")* *$)", std::regex_constants::optimize); - std::smatch results; bool has_ISBN = false; bool has_name = false; bool has_author = false; @@ -15,7 +220,7 @@ bool CommandShowLexer(const std::string &command, std::string &ISBN, name = ""; author = ""; keyword = ""; - if (std::regex_match(command, results, main_pattern)) { + if (std::regex_match(command, main_pattern)) { std::stringstream ss(command); std::string token; ss >> token; @@ -39,6 +244,180 @@ bool CommandShowLexer(const std::string &command, std::string &ISBN, } else return false; } + for (int i = 0; i < keyword.length(); i++) + if (keyword[i] == '|') return false; + return true; + } else + return false; +} + +// clang-format off +/** + * @brief Lexer for command "buy" + * + * @param command The command to be parsed + * @param ISBN The ISBN of the book to be bought + * @param quantity The quantity of the book to be bought + * + * @return true if the command is valid + * + * @note The command is valid if and only if it follows the following grammar: + * buy [ISBN] [Quantity] +*/ +// clang-format on +bool CommandBuyLexer(const std::string &command, std::string &ISBN, + int &quantity) { + static std::basic_regex main_pattern( + R"(^ *buy +(?:\S{1,20}) +[0-9]{1,10} *$)", + std::regex_constants::optimize); + if (std::regex_match(command, main_pattern)) { + std::stringstream ss(command); + std::string token; + ss >> token; + ISBN = ""; + quantity = 0; + ss >> ISBN; + ss >> quantity; + return true; + } else + return false; +} + +// clang-format off +/** + * @brief Lexer for command "select" + * + * @param command The command to be parsed + * @param ISBN The ISBN of the book to be selected + * + * @return true if the command is valid + * + * @note The command is valid if and only if it follows the following grammar: + * select [ISBN] +*/ +// clang-format on +bool CommandSelectLexer(const std::string &command, std::string &ISBN) { + static std::basic_regex main_pattern(R"(^ *select +(?:\S{1,20}) *$)", + std::regex_constants::optimize); + if (std::regex_match(command, main_pattern)) { + std::stringstream ss(command); + std::string token; + ss >> token; + ISBN = ""; + ss >> ISBN; + return true; + } else + return false; +} + +// clang-format off +/** + * @brief Lexer for command "modify" + * + * @param command The command to be parsed + * @param ISBN The ISBN of the book to be modified + * @param name The name of the book to be modified + * @param author The author of the book to be modified + * @param keyword The keyword of the book to be modified + * @param price The price of the book to be modified + * + * @return true if the command is valid + * + * @note The command is valid if and only if it follows the following grammar: + * modify (-ISBN=[ISBN] | -name="[BookName]" | -author="[Author]" | -keyword="[Keyword]" | -price=[Price])+ +*/ +// clang-format on +bool CommandModifyLexer(const std::string &command, std::string &ISBN, + std::string &name, std::string &author, + std::string &keyword, double &price) { + static std::basic_regex main_pattern( + R"(^ *modify(?: +-ISBN=(?:\S{1,20})| +-name=\"(?:[^\s"]{1,60})\"| +-author=\"(?:[^\s"]{1,60})\"| +-keyword=\"(?:[^\s"]{1,60})\"| +-price=[0-9]{1,10}\.[0-9]{2})+ *$)", + std::regex_constants::optimize); + if (std::regex_match(command, main_pattern)) { + std::stringstream ss(command); + std::string token; + ss >> token; + ISBN = ""; + name = ""; + author = ""; + keyword = ""; + price = 0; + while (ss >> token) { + if (token[1] == 'I') { + ISBN = token.substr(6); + } else if (token[1] == 'n') { + name = token.substr(6 + 1, token.size() - 7 - 1); + } else if (token[1] == 'a') { + author = token.substr(8 + 1, token.size() - 9 - 1); + } else if (token[1] == 'k') { + keyword = token.substr(9 + 1, token.size() - 10 - 1); + } else if (token[1] == 'p') { + price = std::stod(token.substr(7)); + } else + return false; + } + for (int i = 0; i < keyword.length(); i++) + if (keyword[i] == '|') return false; + return true; + } else + return false; +} + +// clang-format off +/** + * @brief Lexer for command "import" + * + * @param command The command to be parsed + * @param quantity The quantity of the book to be imported + * @param total_cost The total cost of the book to be imported + * + * @return true if the command is valid + * + * @note The command is valid if and only if it follows the following grammar: + * import [Quantity] [TotalCost] +*/ +// clang-format on +bool CommandImportLexer(const std::string &command, int &quantity, + double &total_cost) { + static std::basic_regex main_pattern( + R"(^ *import +[0-9]{1,10} +[0-9]{1,10}\.[0-9]{2} *$)", + std::regex_constants::optimize); + if (std::regex_match(command, main_pattern)) { + std::stringstream ss(command); + std::string token; + ss >> token; + quantity = 0; + total_cost = 0; + ss >> quantity; + ss >> total_cost; + return true; + } else + return false; +} + +// clang-format off +/** + * @brief Lexer for command "showfinance" + * + * @param command The command to be parsed + * @param count The count of the finance to be shown + * + * @return true if the command is valid + * + * @note The command is valid if and only if it follows the following grammar: + * show finance ([Count])? +*/ +// clang-format on +bool CommandShowfinanceLexer(const std::string &command, int &count) { + static std::basic_regex main_pattern( + R"(^ *show +finance(?: +[0-9]{1,10})? *$)", + std::regex_constants::optimize); + if (std::regex_match(command, main_pattern)) { + std::stringstream ss(command); + std::string token; + ss >> token; + count = -1; + ss >> count; return true; } else return false;