postHeaders = { {"Content-Type", "application/json"} };
+ auto postResponse = client.Post("/post", postData, postHeaders);
+ std::cout << "Status: " << postResponse.statusCode << " " << postResponse.body << std::endl;
+
+ client.AsyncRequest(NahidaProject::ClientHTTPMethod::GET, "/uuid", [](const NahidaProject::ClientHTTPResponse& resp) {
+ std::cout << "Async response received!" << std::endl;
+ std::cout << "Status: " << resp.statusCode << std::endl;
+ });
+
+ std::this_thread::sleep_for(std::chrono::seconds(2));
+ }
+ catch (const std::exception& e) {
+ std::cerr << "Error: " << e.what() << std::endl;
+ }
+}
+
+Test(HTTPServerTest) {
+ NahidaProject::HTTPServer server("127.0.0.1", 8080);
+
+ server.ActionGet("/", [](const NahidaProject::HTTPServer::HTTPRequest& req, NahidaProject::HTTPServer::HTTPResponse& res) {
+ res.body = R"(
+
+
+
+ Simple HTTP Server
+
+
+
+ Welcome to Simple HTTP Server!
+ This is a cross-platform HTTP server written in C++ with Lambda support.
+
+
+
+ )";
+ res.headers["Content-Type"] = "text/html; charset=utf-8";
+ });
+
+ server.ActionGet("/hello", [](const NahidaProject::HTTPServer::HTTPRequest& req, NahidaProject::HTTPServer::HTTPResponse& res) {
+ res.body = "Hello, World!
";
+ res.headers["Content-Type"] = "text/html; charset=utf-8";
+ });
+
+ server.ActionGet("/api/users", [](const NahidaProject::HTTPServer::HTTPRequest& req, NahidaProject::HTTPServer::HTTPResponse& res) {
+ res.body = R"({
+ "users": [
+ {"id": 1, "name": "Alice"},
+ {"id": 2, "name": "Bob"},
+ {"id": 3, "name": "Charlie"}
+ ]
+})";
+ res.headers["Content-Type"] = "application/json; charset=utf-8";
+ });
+
+ server.ActionGet("/query", [](const NahidaProject::HTTPServer::HTTPRequest& req, NahidaProject::HTTPServer::HTTPResponse& res) {
+ std::ostringstream oss;
+ oss << "Query Parameters
";
+ for (const auto& param : req.queryParams) {
+ oss << "- " << param.first << " = " << param.second << "
";
+ }
+ oss << "
";
+ res.body = oss.str();
+ res.headers["Content-Type"] = "text/html; charset=utf-8";
+ });
+
+ server.ActionPost("/api/users", [](const NahidaProject::HTTPServer::HTTPRequest& req, NahidaProject::HTTPServer::HTTPResponse& res) {
+ res.body = R"({"status": "success", "message": "User created"})";
+ res.headers["Content-Type"] = "application/json; charset=utf-8";
+ res.statusCode = 201;
+ res.statusText = "Created";
+ });
+
+ server.ActionGet("/error", [](const NahidaProject::HTTPServer::HTTPRequest& req, NahidaProject::HTTPServer::HTTPResponse& res) {
+ res.statusCode = 500;
+ res.statusText = "Internal Server Error";
+ res.body = "500 Internal Server Error
Something went wrong!
";
+ });
+
+ std::cout << "Starting server..." << std::endl;
+ std::cout << "Press Ctrl+C to stop the server" << std::endl;
+
+ std::thread server_thread([&server]() {
+ server.StartServer();
+ });
+
+ std::cout << "Server is running. Press Enter to stop..." << std::endl;
+ std::cin.get();
+
+ server.StopServer();
+ if (server_thread.joinable()) {
+ server_thread.join();
+ }
+
+ std::cout << "Server stopped." << std::endl;
+}
+
+Test(SerialPortTest) {
+ NahidaProject::SerialPort serial;
+ NahidaProject::SerialPort serial2;
+
+ auto result = serial.OpenSerialPort("COM3"); // Linux: "/dev/ttyUSB0", Windows: "COM1"
+ auto result2 = serial2.OpenSerialPort("COM4"); // Linux: "/dev/ttyUSB0", Windows: "COM1"
+ if (result != NahidaProject::SerialPort::ErrorCode::SUCCESS or result2 != NahidaProject::SerialPort::ErrorCode::SUCCESS) {
+ std::cout << "Failed to open port: " << NahidaProject::SerialPort::GetErrorString(result) << std::endl;
+ }
+
+ result = serial.Configure(NahidaProject::SerialPort::BaudRate::BAUDRATE_115200, NahidaProject::SerialPort::DataBits::DATA_8, NahidaProject::SerialPort::StopBits::STOP_1, NahidaProject::SerialPort::Parity::NONE, NahidaProject::SerialPort::FlowControl::NONE);
+ result2 = serial.Configure(NahidaProject::SerialPort::BaudRate::BAUDRATE_115200, NahidaProject::SerialPort::DataBits::DATA_8, NahidaProject::SerialPort::StopBits::STOP_1, NahidaProject::SerialPort::Parity::NONE, NahidaProject::SerialPort::FlowControl::NONE);
+
+ if (result != NahidaProject::SerialPort::ErrorCode::SUCCESS or result2 != NahidaProject::SerialPort::ErrorCode::SUCCESS) {
+ std::cout << "Failed to configure port: " << NahidaProject::SerialPort::GetErrorString(result) << std::endl;
+ }
+
+ serial2.SetDataReceivedCallback([](const std::vector& data) {
+ std::cout << "Received: ";
+ for (uint8_t byte : data) {
+ printf("%02X ", byte);
+ }
+ std::cout << std::endl;
+ });
+
+ serial2.SetErrorCallback([](NahidaProject::SerialPort::ErrorCode error) {
+ std::cout << "Serial error: " << NahidaProject::SerialPort::GetErrorString(error) << std::endl;
+ });
+
+ serial2.StartAsyncRead();
+
+ // 发送数据测试
+ std::string testData = "Hello Serial Port!";
+ result = serial.WriteString(testData);
+ if (result == NahidaProject::SerialPort::ErrorCode::SUCCESS) {
+ std::cout << "Data sent successfully" << std::endl;
+ }
+
+ std::vector receivedData;
+ result2 = serial2.Read(receivedData, 1024, 1000); // 1秒超时
+ if (result2 == NahidaProject::SerialPort::ErrorCode::SUCCESS) {
+ std::cout << "Received " << receivedData.size() << " bytes" << std::endl;
+ }
+
+ std::this_thread::sleep_for(std::chrono::seconds(10));
+
+ serial2.StopAsyncRead();
+ serial2.CloseSerialPort();
+
+ serial.StopAsyncRead();
+ serial.CloseSerialPort();
+}
+
+Test(StaticWebServerTest) {
+ try {
+ NahidaProject::StaticWebServer server(8081, "www");
+
+ std::cout << "Starting server on port 8080, serving directory: www" << std::endl;
+ std::cout << "Press Ctrl+C to stop the server" << std::endl;
+
+ server.StartServer();
+
+ }
+ catch (const std::exception& e) {
+ std::cerr << "Error: " << e.what() << std::endl;
+ }
+}
+
+int main() {
+ RunAllTestCase();
+ return 0;
+}
diff --git a/NahidaProject.Console/CMakeLists.txt b/NahidaProject.Console/CMakeLists.txt
index fd22946f15b4bdf0b05414c30c2c7f6655755265..7cd15f2215244ab540ca618efcaf9b163d8e37ac 100644
--- a/NahidaProject.Console/CMakeLists.txt
+++ b/NahidaProject.Console/CMakeLists.txt
@@ -4,4 +4,4 @@ CMAKE_MINIMUM_REQUIRED(VERSION 3.31 FATAL_ERROR)
SET(CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/CMakeModules)
INCLUDE(RegisterSubproject)
-REGISTER_SUBPROJECT("Console")
\ No newline at end of file
+REGISTER_SUBPROJECT("Console" FALSE)
\ No newline at end of file
diff --git a/NahidaProject.Database/CMakeLists.txt b/NahidaProject.Database/CMakeLists.txt
index 032538372fb5bb6cac8f690f9b9be4329b4836d1..a2e48a07854afef2d124e3fc9f6e9b17d47c65ef 100644
--- a/NahidaProject.Database/CMakeLists.txt
+++ b/NahidaProject.Database/CMakeLists.txt
@@ -4,8 +4,10 @@ CMAKE_MINIMUM_REQUIRED(VERSION 3.31 FATAL_ERROR)
SET(CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/CMakeModules)
INCLUDE(RegisterSubproject)
-REGISTER_SUBPROJECT("Database")
+REGISTER_SUBPROJECT("Database" FALSE)
-TARGET_LINK_LIBRARIES(NahidaProject.DatabaseTests PUBLIC odbc32)
-TARGET_LINK_LIBRARIES(NahidaProject.Database.IMPLEMENT PUBLIC odbc32)
-TARGET_LINK_LIBRARIES(NahidaProject.Database PUBLIC odbc32)
+IF(WIN32)
+ TARGET_LINK_LIBRARIES(NahidaProject.DatabaseTests PUBLIC odbc32)
+ TARGET_LINK_LIBRARIES(NahidaProject.Database.IMPLEMENT PUBLIC odbc32)
+ TARGET_LINK_LIBRARIES(NahidaProject.Database PUBLIC odbc32)
+ENDIF()
diff --git a/NahidaProject.Database/Sources/ODBCConnection.cpp b/NahidaProject.Database/Sources/ODBCConnection.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..f0aa8a060c3ced2944685df231752f4471a74728
--- /dev/null
+++ b/NahidaProject.Database/Sources/ODBCConnection.cpp
@@ -0,0 +1,284 @@
+#include "ODBCConnection.h"
+
+#include
+#include
+
+ODBCConnection::ODBCConnection() : environmentHandle(SQL_NULL_HENV), databaseHandle(SQL_NULL_HDBC), connected(false) {
+ Initialize();
+}
+
+ODBCConnection::~ODBCConnection() {
+ if (connected) {
+ Disconnect();
+ }
+ CleanUp();
+}
+
+void ODBCConnection::Initialize() {
+ SQLRETURN ret;
+
+ ret = SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &environmentHandle);
+ if (ret != SQL_SUCCESS && ret != SQL_SUCCESS_WITH_INFO) {
+ throw ODBCException("Failed to allocate environment handle");
+ }
+
+ ret = SQLSetEnvAttr(environmentHandle, SQL_ATTR_ODBC_VERSION, (void*)SQL_OV_ODBC3, 0);
+ if (ret != SQL_SUCCESS && ret != SQL_SUCCESS_WITH_INFO) {
+ throw ODBCException("Failed to set ODBC version");
+ }
+
+ ret = SQLAllocHandle(SQL_HANDLE_DBC, environmentHandle, &databaseHandle);
+ if (ret != SQL_SUCCESS && ret != SQL_SUCCESS_WITH_INFO) {
+ throw ODBCException("Failed to allocate connection handle");
+ }
+}
+
+void ODBCConnection::CleanUp() {
+ if (databaseHandle != SQL_NULL_HDBC) {
+ SQLFreeHandle(SQL_HANDLE_DBC, databaseHandle);
+ databaseHandle = SQL_NULL_HDBC;
+ }
+ if (environmentHandle != SQL_NULL_HENV) {
+ SQLFreeHandle(SQL_HANDLE_ENV, environmentHandle);
+ environmentHandle = SQL_NULL_HENV;
+ }
+}
+
+void ODBCConnection::Connect(const std::string& dsn, const std::string& username,
+ const std::string& password) {
+ if (connected) {
+ throw ODBCException("Already connected to database");
+ }
+
+ SQLRETURN ret;
+ SQLCHAR outString[1024];
+ SQLSMALLINT outStringLength;
+
+ ret = SQLConnect(databaseHandle, (SQLCHAR*)dsn.c_str(), SQL_NTS, (SQLCHAR*)(username.empty() ? nullptr : username.c_str()), SQL_NTS, (SQLCHAR*)(password.empty() ? nullptr : password.c_str()), SQL_NTS);
+ CheckError(ret, databaseHandle, SQL_HANDLE_DBC, "Connect to database");
+
+ connected = true;
+}
+
+void ODBCConnection::ConnectWithConnectionString(const std::string& connectionString) {
+ if (connected) {
+ throw ODBCException("Already connected to database");
+ }
+
+ SQLRETURN ret;
+
+ ret = SQLDriverConnect(databaseHandle, nullptr, (SQLCHAR*)connectionString.c_str(), SQL_NTS, nullptr, 0, nullptr, SQL_DRIVER_COMPLETE);
+ CheckError(ret, databaseHandle, SQL_HANDLE_DBC, "Connect with connection string");
+ connected = true;
+}
+
+void ODBCConnection::Disconnect() {
+ if (connected) {
+ SQLDisconnect(databaseHandle);
+ connected = false;
+ }
+}
+
+bool ODBCConnection::IsConnected() const {
+ return connected;
+}
+
+void ODBCConnection::CheckError(SQLRETURN ret, SQLHANDLE handle, SQLSMALLINT type,
+ const std::string& operation) {
+ if (ret == SQL_SUCCESS || ret == SQL_SUCCESS_WITH_INFO) {
+ return;
+ }
+
+ SQLCHAR sqlState[6];
+ SQLCHAR message[SQL_MAX_MESSAGE_LENGTH];
+ SQLINTEGER nativeError;
+ SQLSMALLINT messageLength;
+
+ SQLGetDiagRec(type, handle, 1, sqlState, &nativeError, message, sizeof(message), &messageLength);
+
+ std::string errorMessage = "ODBC Error in " + operation + ": " + std::string((char*)message) + " (SQL State: " + std::string((char*)sqlState) + ")";
+ throw ODBCException(errorMessage);
+}
+
+void ODBCConnection::Execute(const std::string& sql) {
+ if (!connected) {
+ throw ODBCException("Not connected to database");
+ }
+
+ SQLHSTMT statement;
+ SQLRETURN ret;
+
+ ret = SQLAllocHandle(SQL_HANDLE_STMT, databaseHandle, &statement);
+ if (ret != SQL_SUCCESS && ret != SQL_SUCCESS_WITH_INFO) {
+ throw ODBCException("Failed to allocate statement handle");
+ }
+
+ ret = SQLExecDirect(statement, (SQLCHAR*)sql.c_str(), SQL_NTS);
+ CheckError(ret, statement, SQL_HANDLE_STMT, "Execute SQL");
+
+ SQLFreeHandle(SQL_HANDLE_STMT, statement);
+}
+
+std::unique_ptr ODBCConnection::Query(const std::string& sql) {
+ if (!connected) {
+ throw ODBCException("Not connected to database");
+ }
+
+ SQLHSTMT statement;
+ SQLRETURN ret;
+
+ ret = SQLAllocHandle(SQL_HANDLE_STMT, databaseHandle, &statement);
+ if (ret != SQL_SUCCESS && ret != SQL_SUCCESS_WITH_INFO) {
+ throw ODBCException("Failed to allocate statement handle");
+ }
+
+ ret = SQLExecDirect(statement, (SQLCHAR*)sql.c_str(), SQL_NTS);
+ if (ret != SQL_SUCCESS && ret != SQL_SUCCESS_WITH_INFO) {
+ SQLFreeHandle(SQL_HANDLE_STMT, statement);
+ CheckError(ret, statement, SQL_HANDLE_STMT, "Execute query");
+ }
+
+ return std::make_unique(statement);
+}
+
+void ODBCConnection::BeginTransaction() {
+ if (!connected) {
+ throw ODBCException("Not connected to database");
+ }
+
+ SQLRETURN ret = SQLSetConnectAttr(databaseHandle, SQL_ATTR_AUTOCOMMIT, SQL_AUTOCOMMIT_OFF, 0);
+ CheckError(ret, databaseHandle, SQL_HANDLE_DBC, "Begin transaction");
+}
+
+void ODBCConnection::Commit() {
+ if (!connected) {
+ throw ODBCException("Not connected to database");
+ }
+
+ SQLRETURN ret = SQLEndTran(SQL_HANDLE_DBC, databaseHandle, SQL_COMMIT);
+ CheckError(ret, databaseHandle, SQL_HANDLE_DBC, "Commit transaction");
+
+ ret = SQLSetConnectAttr(databaseHandle, SQL_ATTR_AUTOCOMMIT, SQL_AUTOCOMMIT_ON, 0);
+ CheckError(ret, databaseHandle, SQL_HANDLE_DBC, "Enable autocommit");
+}
+
+void ODBCConnection::Rollback() {
+ if (!connected) {
+ throw ODBCException("Not connected to database");
+ }
+
+ SQLRETURN ret = SQLEndTran(SQL_HANDLE_DBC, databaseHandle, SQL_ROLLBACK);
+ CheckError(ret, databaseHandle, SQL_HANDLE_DBC, "Rollback transaction");
+
+ ret = SQLSetConnectAttr(databaseHandle, SQL_ATTR_AUTOCOMMIT, SQL_AUTOCOMMIT_ON, 0);
+ CheckError(ret, databaseHandle, SQL_HANDLE_DBC, "Enable autocommit");
+}
+
+std::string ODBCConnection::GetLastError() const {
+ SQLCHAR message[SQL_MAX_MESSAGE_LENGTH];
+ SQLCHAR sqlState[6];
+ SQLINTEGER nativeError;
+ SQLSMALLINT messageLength;
+
+ SQLRETURN ret = SQLGetDiagRec(SQL_HANDLE_DBC, databaseHandle, 1, sqlState, &nativeError, message, sizeof(message), &messageLength);
+
+ if (ret == SQL_SUCCESS || ret == SQL_SUCCESS_WITH_INFO) {
+ return std::string((char*)message);
+ }
+
+ return "No error information available";
+}
+
+ODBCConnection::ResultSet::ResultSet(SQLHSTMT statementHandle) : statementHandle(statementHandle) {
+ SQLRETURN ret;
+
+ ret = SQLNumResultCols(statementHandle, &columnCount);
+ if (ret != SQL_SUCCESS && ret != SQL_SUCCESS_WITH_INFO) {
+ throw ODBCException("Failed to get column count");
+ }
+
+ columnData.resize(columnCount);
+ columnIndicators.resize(columnCount);
+
+ for (SQLSMALLINT i = 0; i < columnCount; i++) {
+ SQLSMALLINT columnNameLength;
+ SQLSMALLINT dataType;
+ SQLULEN columnSize;
+ SQLSMALLINT decimalDigits;
+ SQLSMALLINT nullable;
+
+ ret = SQLDescribeCol(statementHandle, i + 1, nullptr, 0, &columnNameLength, &dataType, &columnSize, &decimalDigits, &nullable);
+
+ columnData[i] = new SQLCHAR[1024];
+ ret = SQLBindCol(statementHandle, i + 1, SQL_C_CHAR, columnData[i], 1024,&columnIndicators[i]);
+ if (ret != SQL_SUCCESS && ret != SQL_SUCCESS_WITH_INFO) {
+ throw ODBCException("Failed to bind column");
+ }
+ }
+}
+
+ODBCConnection::ResultSet::~ResultSet() {
+ for (auto& data : columnData) {
+ delete[] data;
+ }
+ if (statementHandle != SQL_NULL_HSTMT) {
+ SQLFreeHandle(SQL_HANDLE_STMT, statementHandle);
+ }
+}
+
+bool ODBCConnection::ResultSet::Next() {
+ SQLRETURN ret = SQLFetch(statementHandle);
+ if (ret == SQL_SUCCESS || ret == SQL_SUCCESS_WITH_INFO) {
+ return true;
+ }
+ else if (ret == SQL_NO_DATA) {
+ return false;
+ }
+ else {
+ throw ODBCException("Failed to fetch next row");
+ }
+}
+
+std::string ODBCConnection::ResultSet::GetString(int column) {
+ if (column < 1 || column > columnCount) {
+ throw ODBCException("Invalid column index");
+ }
+
+ if (IsNull(column)) {
+ return "";
+ }
+
+ return std::string((char*)columnData[column - 1]);
+}
+
+int ODBCConnection::ResultSet::GetInteger(int column) {
+ if (column < 1 || column > columnCount) {
+ throw ODBCException("Invalid column index");
+ }
+
+ if (IsNull(column)) {
+ return 0;
+ }
+
+ return std::stoi((char*)columnData[column - 1]);
+}
+
+double ODBCConnection::ResultSet::GetDouble(int column) {
+ if (column < 1 || column > columnCount) {
+ throw ODBCException("Invalid column index");
+ }
+
+ if (IsNull(column)) {
+ return 0.0;
+ }
+
+ return std::stod((char*)columnData[column - 1]);
+}
+
+bool ODBCConnection::ResultSet::IsNull(int column) {
+ if (column < 1 || column > columnCount) {
+ throw ODBCException("Invalid column index");
+ }
+
+ return columnIndicators[column - 1] == SQL_NULL_DATA;
+}
\ No newline at end of file
diff --git a/NahidaProject.Database/Sources/ODBCConnection.h b/NahidaProject.Database/Sources/ODBCConnection.h
new file mode 100644
index 0000000000000000000000000000000000000000..e207e3b8ac15cfd84b6009a0cec1cc4acd35a1b1
--- /dev/null
+++ b/NahidaProject.Database/Sources/ODBCConnection.h
@@ -0,0 +1,68 @@
+#include
+#include
+#include
+#include
+#include
+
+#ifdef _WIN32
+#include
+#endif
+
+#include
+#include
+
+class ODBCException : public std::runtime_error {
+public:
+ explicit ODBCException(const std::string& message) : std::runtime_error(message) {}
+};
+
+class ODBCConnection {
+public:
+ ODBCConnection();
+ ~ODBCConnection();
+
+ ODBCConnection(const ODBCConnection&) = delete;
+ ODBCConnection& operator=(const ODBCConnection&) = delete;
+
+ void Connect(const std::string& dsn, const std::string& username = "", const std::string& password = "");
+ void ConnectWithConnectionString(const std::string& connectionString);
+ void Disconnect();
+ bool IsConnected() const;
+
+ void Execute(const std::string& sql);
+
+ class ResultSet {
+ public:
+ ResultSet(SQLHSTMT stmt);
+ ~ResultSet();
+
+ bool Next();
+ std::string GetString(int column);
+ int GetInteger(int column);
+ double GetDouble(int column);
+ bool IsNull(int column);
+
+ private:
+ SQLHSTMT statementHandle;
+ SQLSMALLINT columnCount;
+ std::vector columnData;
+ std::vector columnIndicators;
+ };
+
+ std::unique_ptr Query(const std::string& sql);
+
+ void BeginTransaction();
+ void Commit();
+ void Rollback();
+
+ std::string GetLastError() const;
+
+private:
+ SQLHENV environmentHandle;
+ SQLHDBC databaseHandle;
+ bool connected;
+
+ void CheckError(SQLRETURN ret, SQLHANDLE handle, SQLSMALLINT type, const std::string& operation);
+ void Initialize();
+ void CleanUp();
+};
diff --git a/NahidaProject.Generic/CMakeLists.txt b/NahidaProject.Generic/CMakeLists.txt
index c9a738b20b1a1b824d41c3025b3fdc74c04eaed4..92e56ba3e1afd814dfb86b614b7cd112aa4672f3 100644
--- a/NahidaProject.Generic/CMakeLists.txt
+++ b/NahidaProject.Generic/CMakeLists.txt
@@ -4,8 +4,4 @@ CMAKE_MINIMUM_REQUIRED(VERSION 3.31 FATAL_ERROR)
SET(CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/CMakeModules)
INCLUDE(RegisterSubproject)
-REGISTER_SUBPROJECT("Generic")
-
-TARGET_LINK_LIBRARIES(NahidaProject.GenericTests PUBLIC ws2_32)
-TARGET_LINK_LIBRARIES(NahidaProject.Generic.IMPLEMENT PUBLIC ws2_32)
-TARGET_LINK_LIBRARIES(NahidaProject.Generic PUBLIC ws2_32)
+REGISTER_SUBPROJECT("Generic" FALSE)
diff --git a/NahidaProject.Generic/Sources/Date.cpp b/NahidaProject.Generic/Sources/Date.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..84f516a5fcf6c3293fbd820c3f2e088f13065d05
--- /dev/null
+++ b/NahidaProject.Generic/Sources/Date.cpp
@@ -0,0 +1,235 @@
+#include "Date.h"
+
+bool NahidaProject::Date::IsLeapYear(int year) const {
+ return (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0);
+}
+
+int NahidaProject::Date::GetDaysInMonth(int year, int month) const {
+ int daysPerMonth[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
+ if (month == 2 && IsLeapYear(year)) {
+ return 29;
+ }
+ return daysPerMonth[month - 1];
+}
+
+bool NahidaProject::Date::IsValidDate(int y, int m, int d) const {
+ if (m < 1 || m > 12) return false;
+ if (d < 1 || d > GetDaysInMonth(y, m)) return false;
+ return true;
+}
+
+long long NahidaProject::Date::DateToDays() const {
+ long long totalDays = 0;
+ for (int i = 1; i < year; ++i) {
+ totalDays += IsLeapYear(i) ? 366 : 365;
+ }
+ for (int i = 1; i < month; ++i) {
+ totalDays += GetDaysInMonth(year, i);
+ }
+ totalDays += day;
+ return totalDays;
+}
+
+// 赋值运算符
+NahidaProject::Date& NahidaProject::Date::operator=(const Date& other) {
+ if (this != &other) {
+ year = other.year;
+ month = other.month;
+ day = other.day;
+ }
+ return *this;
+}
+
+int NahidaProject::Date::GetYear() const { return year; }
+int NahidaProject::Date::GetMonth() const { return month; }
+int NahidaProject::Date::GetDay() const { return day; }
+
+void NahidaProject::Date::SetDate(int y, int m, int d) {
+ if (!IsValidDate(y, m, d)) {
+ throw std::invalid_argument("Invalid date");
+ }
+ year = y;
+ month = m;
+ day = d;
+}
+
+bool NahidaProject::Date::operator==(const Date& other) const {
+ return year == other.year && month == other.month && day == other.day;
+}
+
+bool NahidaProject::Date::operator!=(const Date& other) const {
+ return !(*this == other);
+}
+
+bool NahidaProject::Date::operator<(const Date& other) const {
+ if (year != other.year) return year < other.year;
+ if (month != other.month) return month < other.month;
+ return day < other.day;
+}
+
+bool NahidaProject::Date::operator<=(const Date& other) const {
+ return (*this < other) || (*this == other);
+}
+
+bool NahidaProject::Date::operator>(const Date& other) const {
+ return !(*this <= other);
+}
+
+bool NahidaProject::Date::operator>=(const Date& other) const {
+ return !(*this < other);
+}
+
+NahidaProject::Date NahidaProject::Date::operator+(int days) const {
+ Date result(*this);
+ result.AddDays(days);
+ return result;
+}
+
+NahidaProject::Date NahidaProject::Date::operator-(int days) const {
+ Date result(*this);
+ result.AddDays(-days);
+ return result;
+}
+
+NahidaProject::Date& NahidaProject::Date::operator+=(int days) {
+ AddDays(days);
+ return *this;
+}
+
+NahidaProject::Date& NahidaProject::Date::operator-=(int days) {
+ AddDays(-days);
+ return *this;
+}
+
+int NahidaProject::Date::operator-(const Date& other) const {
+ return static_cast(DateToDays() - other.DateToDays());
+}
+
+NahidaProject::Date& NahidaProject::Date::operator++() {
+ AddDays(1);
+ return *this;
+}
+
+NahidaProject::Date& NahidaProject::Date::operator--() {
+ AddDays(-1);
+ return *this;
+}
+
+NahidaProject::Date NahidaProject::Date::operator++(int) {
+ Date temp(*this);
+ AddDays(1);
+ return temp;
+}
+
+NahidaProject::Date NahidaProject::Date::operator--(int) {
+ Date temp(*this);
+ AddDays(-1);
+ return temp;
+}
+
+void NahidaProject::Date::AddDays(int days) {
+ long long currentDays = DateToDays();
+ long long newDays = currentDays + days;
+
+ if (newDays <= 0) {
+ throw std::invalid_argument("Resulting date would be before year 1");
+ }
+
+ year = 1;
+ while (newDays > (IsLeapYear(year) ? 366 : 365)) {
+ newDays -= IsLeapYear(year) ? 366 : 365;
+ year++;
+ }
+
+ month = 1;
+ while (newDays > GetDaysInMonth(year, month)) {
+ newDays -= GetDaysInMonth(year, month);
+ month++;
+ }
+
+ day = static_cast(newDays);
+}
+
+void NahidaProject::Date::AddMonths(int months) {
+ int totalMonths = month + months - 1;
+ int yearsToAdd = totalMonths / 12;
+
+ year += yearsToAdd;
+ month = (totalMonths % 12) + 1;
+
+ if (day > GetDaysInMonth(year, month)) {
+ day = GetDaysInMonth(year, month);
+ }
+}
+
+void NahidaProject::Date::AddYears(int years) {
+ year += years;
+ if (month == 2 && day == 29 && !IsLeapYear(year)) {
+ day = 28;
+ }
+}
+
+int NahidaProject::Date::GetWeekday() const {
+ int y = year;
+ int m = month;
+ int d = day;
+
+ if (m < 3) {
+ m += 12;
+ y--;
+ }
+
+ int k = y % 100;
+ int j = y / 100;
+
+ int h = (d + (13 * (m + 1)) / 5 + k + k / 4 + j / 4 - 2 * j) % 7;
+ return (h + 5) % 7;
+}
+
+std::string NahidaProject::Date::GetWeekdayString() const {
+ std::string weekdays[] = { "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" };
+ return weekdays[GetWeekday()];
+}
+
+std::string NahidaProject::Date::ToString(const std::string& format) const {
+ std::string result = format;
+
+ size_t position = result.find("YYYY");
+ if (position != std::string::npos) {
+ result.replace(position, 4, std::to_string(year));
+ }
+
+ position = result.find("MM");
+ if (position != std::string::npos) {
+ std::string monthString = (month < 10) ? "0" + std::to_string(month) : std::to_string(month);
+ result.replace(position, 2, monthString);
+ }
+
+ position = result.find("DD");
+ if (position != std::string::npos) {
+ std::string dayString = (day < 10) ? "0" + std::to_string(day) : std::to_string(day);
+ result.replace(position, 2, dayString);
+ }
+
+ return result;
+}
+
+bool NahidaProject::Date::IsEndOfMonth() const {
+ return day == GetDaysInMonth(year, month);
+}
+
+NahidaProject::Date NahidaProject::Date::EndOfMonth() const {
+ return Date(year, month, GetDaysInMonth(year, month));
+}
+
+NahidaProject::Date NahidaProject::Date::StartOfMonth() const {
+ return Date(year, month, 1);
+}
+
+NahidaProject::Date NahidaProject::Date::EndOfYear() const {
+ return Date(year, 12, 31);
+}
+
+NahidaProject::Date NahidaProject::Date::StartOfYear() const {
+ return Date(year, 1, 1);
+}
diff --git a/NahidaProject.Generic/Sources/Date.h b/NahidaProject.Generic/Sources/Date.h
new file mode 100644
index 0000000000000000000000000000000000000000..81225790e5daee8e35c75453a1a11aedcad8db96
--- /dev/null
+++ b/NahidaProject.Generic/Sources/Date.h
@@ -0,0 +1,67 @@
+#include
+#include
+#include
+#include
+
+namespace NahidaProject {
+ class Date {
+ private:
+ int year;
+ int month;
+ int day;
+ bool IsLeapYear(int year) const;
+ int GetDaysInMonth(int year, int month) const;
+ bool IsValidDate(int y, int m, int d) const;
+ long long DateToDays() const;
+
+ public:
+ Date() : year(1970), month(1), day(1) {
+
+ }
+
+ Date(int y, int m, int d) {
+ if (!IsValidDate(y, m, d)) {
+ throw std::invalid_argument("Invalid date");
+ }
+ year = y;
+ month = m;
+ day = d;
+ }
+
+ Date(const Date& other) : year(other.year), month(other.month), day(other.day) {
+
+ }
+
+ Date& operator=(const Date& other);
+ int GetYear() const;
+ int GetMonth() const;
+ int GetDay() const;
+ void SetDate(int y, int m, int d);
+ bool operator==(const Date& other) const;
+ bool operator!=(const Date& other) const;
+ bool operator<(const Date& other) const;
+ bool operator<=(const Date& other) const;
+ bool operator>(const Date& other) const;
+ bool operator>=(const Date& other) const;
+ Date operator+(int days) const;
+ Date operator-(int days) const;
+ Date& operator+=(int days);
+ Date& operator-=(int days);
+ int operator-(const Date& other) const;
+ Date& operator++();
+ Date& operator--();
+ Date operator++(int);
+ Date operator--(int);
+ void AddDays(int days);
+ void AddMonths(int months);
+ void AddYears(int years);
+ int GetWeekday() const;
+ std::string GetWeekdayString() const;
+ std::string ToString(const std::string& format = "YYYY-MM-DD") const;
+ bool IsEndOfMonth() const;
+ Date EndOfMonth() const;
+ Date StartOfMonth() const;
+ Date EndOfYear() const;
+ Date StartOfYear() const;
+ };
+}
diff --git a/NahidaProject.Generic/Sources/DateTime.cpp b/NahidaProject.Generic/Sources/DateTime.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..a9c6ae8a69ed2bda4add25657f2de38444455970
--- /dev/null
+++ b/NahidaProject.Generic/Sources/DateTime.cpp
@@ -0,0 +1,245 @@
+#include "DateTime.h"
+
+bool NahidaProject::DateTime::IsLeapYear(int year) const {
+ return (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0);
+}
+
+int NahidaProject::DateTime::GetDaysInMonth(int year, int month) const {
+ static const int days[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
+ if (month == 2 && IsLeapYear(year)) {
+ return 29;
+ }
+ return days[month - 1];
+}
+
+void NahidaProject::DateTime::Validate() const {
+ if (year < 1 || year > 9999) {
+ throw std::invalid_argument("Invalid year");
+ }
+ if (month < 1 || month > 12) {
+ throw std::invalid_argument("Invalid month");
+ }
+ if (day < 1 || day > GetDaysInMonth(year, month)) {
+ throw std::invalid_argument("Invalid day");
+ }
+ if (hour < 0 || hour > 23) {
+ throw std::invalid_argument("Invalid hour");
+ }
+ if (minute < 0 || minute > 59) {
+ throw std::invalid_argument("Invalid minute");
+ }
+ if (second < 0 || second > 59) {
+ throw std::invalid_argument("Invalid second");
+ }
+}
+
+long long NahidaProject::DateTime::ToJulianDay() const {
+ int a = (14 - month) / 12;
+ int y = year + 4800 - a;
+ int m = month + 12 * a - 3;
+ return day + (153 * m + 2) / 5 + 365 * y + y / 4 - y / 100 + y / 400 - 32045;
+}
+
+void NahidaProject::DateTime::FromJulianDay(long long jd) {
+ long long a = jd + 32044;
+ long long b = (4 * a + 3) / 146097;
+ long long c = a - (146097 * b) / 4;
+ long long d = (4 * c + 3) / 1461;
+ long long e = c - (1461 * d) / 4;
+ long long m = (5 * e + 2) / 153;
+
+ day = e - (153 * m + 2) / 5 + 1;
+ month = m + 3 - 12 * (m / 10);
+ year = 100 * b + d - 4800 + m / 10;
+}
+
+NahidaProject::DateTime& NahidaProject::DateTime::operator=(const DateTime& other) {
+ if (this != &other) {
+ year = other.year;
+ month = other.month;
+ day = other.day;
+ hour = other.hour;
+ minute = other.minute;
+ second = other.second;
+ }
+ return *this;
+}
+
+NahidaProject::DateTime NahidaProject::DateTime::Now() {
+ std::time_t t = std::time(nullptr);
+ std::tm* localTime = std::localtime(&t);
+ return DateTime(
+ localTime->tm_year + 1900,
+ localTime->tm_mon + 1,
+ localTime->tm_mday,
+ localTime->tm_hour,
+ localTime->tm_min,
+ localTime->tm_sec
+ );
+}
+
+NahidaProject::DateTime NahidaProject::DateTime::Parse(const std::string& str, const std::string& format) {
+ std::tm time = {};
+ std::istringstream ss(str);
+ ss >> std::get_time(&time, format.c_str());
+ if (ss.fail()) {
+ throw std::invalid_argument("Failed to parse date time string");
+ }
+ return DateTime(
+ time.tm_year + 1900,
+ time.tm_mon + 1,
+ time.tm_mday,
+ time.tm_hour,
+ time.tm_min,
+ time.tm_sec
+ );
+}
+
+int NahidaProject::DateTime::GetYear() const { return year; }
+int NahidaProject::DateTime::GetMonth() const { return month; }
+int NahidaProject::DateTime::GetDay() const { return day; }
+int NahidaProject::DateTime::GetHour() const { return hour; }
+int NahidaProject::DateTime::GetMinute() const { return minute; }
+int NahidaProject::DateTime::GetSecond() const { return second; }
+
+void NahidaProject::DateTime::SetYear(int y) {
+ DateTime temp(y, month, day, hour, minute, second);
+ *this = temp;
+}
+
+void NahidaProject::DateTime::SetMonth(int m) {
+ DateTime temp(year, m, day, hour, minute, second);
+ *this = temp;
+}
+
+void NahidaProject::DateTime::SetDay(int d) {
+ DateTime temp(year, month, d, hour, minute, second);
+ *this = temp;
+}
+
+void NahidaProject::DateTime::SetHour(int h) {
+ DateTime temp(year, month, day, h, minute, second);
+ *this = temp;
+}
+
+void NahidaProject::DateTime::SetMinute(int min) {
+ DateTime temp(year, month, day, hour, min, second);
+ *this = temp;
+}
+
+void NahidaProject::DateTime::SetSecond(int s) {
+ DateTime temp(year, month, day, hour, minute, s);
+ *this = temp;
+}
+
+std::string NahidaProject::DateTime::ToString(const std::string& format) const {
+ std::tm time = {};
+ time.tm_year = year - 1900;
+ time.tm_mon = month - 1;
+ time.tm_mday = day;
+ time.tm_hour = hour;
+ time.tm_min = minute;
+ time.tm_sec = second;
+
+ std::ostringstream ss;
+ ss << std::put_time(&time, format.c_str());
+ return ss.str();
+}
+
+double NahidaProject::DateTime::operator-(const DateTime& other) const {
+ long long differentDays = ToJulianDay() - other.ToJulianDay();
+ long long differentSeconds = differentDays * 24 * 3600;
+ differentSeconds += (hour - other.hour) * 3600;
+ differentSeconds += (minute - other.minute) * 60;
+ differentSeconds += (second - other.second);
+ return static_cast(differentSeconds);
+}
+
+NahidaProject::DateTime NahidaProject::DateTime::operator+(int seconds) const {
+ DateTime result(*this);
+ result.AddSeconds(seconds);
+ return result;
+}
+
+NahidaProject::DateTime NahidaProject::DateTime::operator-(int seconds) const {
+ DateTime result(*this);
+ result.AddSeconds(-seconds);
+ return result;
+}
+
+NahidaProject::DateTime& NahidaProject::DateTime::operator+=(int seconds) {
+ AddSeconds(seconds);
+ return *this;
+}
+
+NahidaProject::DateTime& NahidaProject::DateTime::operator-=(int seconds) {
+ AddSeconds(-seconds);
+ return *this;
+}
+
+void NahidaProject::DateTime::AddSeconds(int seconds) {
+ long long totalSeconds = static_cast(ToJulianDay()) * 24 * 3600 + hour * 3600 + minute * 60 + second + seconds;
+ long long days = totalSeconds / (24 * 3600);
+ long long remainingSeconds = totalSeconds % (24 * 3600);
+
+ if (remainingSeconds < 0) {
+ days--;
+ remainingSeconds += 24 * 3600;
+ }
+
+ FromJulianDay(days);
+
+ hour = remainingSeconds / 3600;
+ remainingSeconds %= 3600;
+ minute = remainingSeconds / 60;
+ second = remainingSeconds % 60;
+
+ Validate();
+}
+
+bool NahidaProject::DateTime::operator==(const DateTime& other) const {
+ return year == other.year && month == other.month && day == other.day && hour == other.hour && minute == other.minute && second == other.second;
+}
+
+bool NahidaProject::DateTime::operator!=(const DateTime& other) const {
+ return !(*this == other);
+}
+
+bool NahidaProject::DateTime::operator<(const DateTime& other) const {
+ if (year != other.year) return year < other.year;
+ if (month != other.month) return month < other.month;
+ if (day != other.day) return day < other.day;
+ if (hour != other.hour) return hour < other.hour;
+ if (minute != other.minute) return minute < other.minute;
+ return second < other.second;
+}
+
+bool NahidaProject::DateTime::operator<=(const DateTime& other) const {
+ return *this < other || *this == other;
+}
+
+bool NahidaProject::DateTime::operator>(const DateTime& other) const {
+ return !(*this <= other);
+}
+
+bool NahidaProject::DateTime::operator>=(const DateTime& other) const {
+ return !(*this < other);
+}
+
+int NahidaProject::DateTime::GetWeekday() const {
+ long long jd = ToJulianDay();
+ return (jd + 1) % 7;
+}
+
+int NahidaProject::DateTime::GetDayOfYear() const {
+ int days = 0;
+ for (int i = 1; i < month; i++) {
+ days += GetDaysInMonth(year, i);
+ }
+ days += day;
+ return days;
+}
+
+bool NahidaProject::DateTime::IsLeapYear() const {
+ return IsLeapYear(year);
+}
diff --git a/NahidaProject.Generic/Sources/DateTime.h b/NahidaProject.Generic/Sources/DateTime.h
new file mode 100644
index 0000000000000000000000000000000000000000..8d2b7ff4d6887fe4f33917f7b488e5703979559a
--- /dev/null
+++ b/NahidaProject.Generic/Sources/DateTime.h
@@ -0,0 +1,70 @@
+#include
+#include
+#include
+#include
+#include
+#include
+
+namespace NahidaProject {
+ class DateTime {
+ private:
+ int year;
+ int month;
+ int day;
+ int hour;
+ int minute;
+ int second;
+
+ bool IsLeapYear(int year) const;
+ int GetDaysInMonth(int year, int month) const;
+ void Validate() const;
+ long long ToJulianDay() const;
+ void FromJulianDay(long long jd);
+
+ public:
+ DateTime() : year(1970), month(1), day(1), hour(0), minute(0), second(0) {
+
+ }
+
+ DateTime(int y, int m, int d, int h = 0, int min = 0, int s = 0) : year(y), month(m), day(d), hour(h), minute(min), second(s) {
+ Validate();
+ }
+
+ DateTime(const DateTime& other) : year(other.year), month(other.month), day(other.day), hour(other.hour), minute(other.minute), second(other.second) {
+
+ }
+
+ DateTime& operator=(const DateTime& other);
+ static DateTime Now();
+ static DateTime Parse(const std::string& str, const std::string& format = "%Y-%m-%d %H:%M:%S");
+ int GetYear() const;
+ int GetMonth() const;
+ int GetDay() const;
+ int GetHour() const;
+ int GetMinute() const;
+ int GetSecond() const;
+
+ void SetYear(int y);
+ void SetMonth(int m);
+ void SetDay(int d);
+ void SetHour(int h);
+ void SetMinute(int min);
+ void SetSecond(int s);
+ std::string ToString(const std::string& format = "%Y-%m-%d %H:%M:%S") const;
+ double operator-(const DateTime& other) const;
+ DateTime operator+(int seconds) const;
+ DateTime operator-(int seconds) const;
+ DateTime& operator+=(int seconds);
+ DateTime& operator-=(int seconds);
+ void AddSeconds(int seconds);
+ bool operator==(const DateTime& other) const;
+ bool operator!=(const DateTime& other) const;
+ bool operator<(const DateTime& other) const;
+ bool operator<=(const DateTime& other) const;
+ bool operator>(const DateTime& other) const;
+ bool operator>=(const DateTime& other) const;
+ int GetWeekday() const;
+ int GetDayOfYear() const;
+ bool IsLeapYear() const;
+ };
+}
diff --git a/NahidaProject.Generic/Sources/FileHelper.cpp b/NahidaProject.Generic/Sources/FileHelper.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..062596de1126e4a769302c17f9dbdc1e1b23fff7
--- /dev/null
+++ b/NahidaProject.Generic/Sources/FileHelper.cpp
@@ -0,0 +1,267 @@
+#include "FileHelper.h"
+
+void NahidaProject::FileHelper::SetFilename(const std::string& targetName) {
+ fileName = targetName;
+}
+
+std::string NahidaProject::FileHelper::GetFilename() const {
+ return fileName;
+}
+
+bool NahidaProject::FileHelper::Exists() const {
+ if (fileName.empty()) {
+ throw std::runtime_error("Filename is empty");
+ }
+ return fs::exists(fileName);
+}
+
+bool NahidaProject::FileHelper::CreateFile() const {
+ if (fileName.empty()) {
+ throw std::runtime_error("Filename is empty");
+ }
+
+ try {
+ std::ofstream ofs(fileName, std::ios::out | std::ios::app);
+ return ofs.good();
+ }
+ catch (const std::exception& e) {
+ throw std::runtime_error("Failed to create file: " + std::string(e.what()));
+ }
+}
+
+void NahidaProject::FileHelper::WriteText(const std::string& content) const {
+ ValidateFilename();
+
+ try {
+ std::ofstream ofs(fileName, std::ios::out | std::ios::trunc);
+ if (!ofs.is_open()) {
+ throw std::runtime_error("Cannot open file for writing");
+ }
+ ofs << content;
+ }
+ catch (const std::exception& e) {
+ throw std::runtime_error("Failed to write text: " + std::string(e.what()));
+ }
+}
+
+void NahidaProject::FileHelper::AppendText(const std::string& content) const {
+ ValidateFilename();
+
+ try {
+ std::ofstream ofs(fileName, std::ios::out | std::ios::app);
+ if (!ofs.is_open()) {
+ throw std::runtime_error("Cannot open file for appending");
+ }
+ ofs << content;
+ }
+ catch (const std::exception& e) {
+ throw std::runtime_error("Failed to append text: " + std::string(e.what()));
+ }
+}
+
+void NahidaProject::FileHelper::WriteLines(const std::vector& lines) const {
+ ValidateFilename();
+
+ try {
+ std::ofstream ofs(fileName, std::ios::out | std::ios::trunc);
+ if (!ofs.is_open()) {
+ throw std::runtime_error("Cannot open file for writing");
+ }
+
+ for (const auto& line : lines) {
+ ofs << line << '\n';
+ }
+ }
+ catch (const std::exception& e) {
+ throw std::runtime_error("Failed to write lines: " + std::string(e.what()));
+ }
+}
+
+std::string NahidaProject::FileHelper::ReadWholeText() const {
+ ValidateFilename();
+
+ try {
+ std::ifstream ifs(fileName, std::ios::in);
+ if (!ifs.is_open()) {
+ throw std::runtime_error("Cannot open file for reading");
+ }
+
+ std::ostringstream buffer;
+ buffer << ifs.rdbuf();
+ return buffer.str();
+ }
+ catch (const std::exception& e) {
+ throw std::runtime_error("Failed to read text: " + std::string(e.what()));
+ }
+}
+
+std::vector NahidaProject::FileHelper::ReadLines() const {
+ ValidateFilename();
+
+ try {
+ std::vector lines;
+ std::ifstream ifs(fileName, std::ios::in);
+ if (!ifs.is_open()) {
+ throw std::runtime_error("Cannot open file for reading");
+ }
+
+ std::string line;
+ while (std::getline(ifs, line)) {
+ lines.push_back(line);
+ }
+
+ return lines;
+ }
+ catch (const std::exception& e) {
+ throw std::runtime_error("Failed to read lines: " + std::string(e.what()));
+ }
+}
+
+std::vector NahidaProject::FileHelper::ReadBinary() const {
+ ValidateFilename();
+
+ try {
+ std::ifstream ifs(fileName, std::ios::binary | std::ios::in);
+ if (!ifs.is_open()) {
+ throw std::runtime_error("Cannot open file for binary reading");
+ }
+
+ ifs.seekg(0, std::ios::end);
+ std::streamsize size = ifs.tellg();
+ ifs.seekg(0, std::ios::beg);
+
+ // 读取数据
+ std::vector buffer(size);
+ ifs.read(buffer.data(), size);
+
+ return buffer;
+ }
+ catch (const std::exception& e) {
+ throw std::runtime_error("Failed to read binary data: " + std::string(e.what()));
+ }
+}
+
+void NahidaProject::FileHelper::WriteBinary(const std::vector& data) const {
+ ValidateFilename();
+
+ try {
+ std::ofstream ofs(fileName, std::ios::binary | std::ios::out | std::ios::trunc);
+ if (!ofs.is_open()) {
+ throw std::runtime_error("Cannot open file for binary writing");
+ }
+
+ ofs.write(data.data(), data.size());
+ }
+ catch (const std::exception& e) {
+ throw std::runtime_error("Failed to write binary data: " + std::string(e.what()));
+ }
+}
+
+bool NahidaProject::FileHelper::CopyFile(const std::string& source, const std::string& destination) {
+ try {
+ return fs::copy_file(source, destination, fs::copy_options::overwrite_existing);
+ }
+ catch (const std::exception& e) {
+ throw std::runtime_error("Failed to copy file: " + std::string(e.what()));
+ }
+}
+
+bool NahidaProject::FileHelper::MoveFile(const std::string& source, const std::string& destination) {
+ try {
+ fs::rename(source, destination);
+ return true;
+ }
+ catch (const std::exception& e) {
+ throw std::runtime_error("Failed to move/rename file: " + std::string(e.what()));
+ }
+}
+
+bool NahidaProject::FileHelper::DeleteFile(const std::string& fname) {
+ try {
+ return fs::remove(fname);
+ }
+ catch (const std::exception& e) {
+ throw std::runtime_error("Failed to delete file: " + std::string(e.what()));
+ }
+}
+
+std::uintmax_t NahidaProject::FileHelper::GetFileSize() const {
+ ValidateFilename();
+
+ try {
+ return fs::file_size(fileName);
+ }
+ catch (const std::exception& e) {
+ throw std::runtime_error("Failed to get file size: " + std::string(e.what()));
+ }
+}
+
+std::time_t NahidaProject::FileHelper::GetLastModified() const {
+ ValidateFilename();
+
+ try {
+ auto ftime = fs::last_write_time(fileName);
+ auto sctp = std::chrono::time_point_cast(ftime - fs::file_time_type::clock::now() + std::chrono::system_clock::now());
+ return std::chrono::system_clock::to_time_t(sctp);
+ }
+ catch (const std::exception& e) {
+ throw std::runtime_error("Failed to get last modified time: " + std::string(e.what()));
+ }
+}
+
+void NahidaProject::FileHelper::ClearFile() const {
+ ValidateFilename();
+
+ try {
+ std::ofstream ofs(fileName, std::ios::out | std::ios::trunc);
+ if (!ofs.is_open()) {
+ throw std::runtime_error("Cannot open file for clearing");
+ }
+ }
+ catch (const std::exception& e) {
+ throw std::runtime_error("Failed to clear file: " + std::string(e.what()));
+ }
+}
+
+bool NahidaProject::FileHelper::IsEmpty() const {
+ ValidateFilename();
+
+ try {
+ return (GetFileSize() == 0);
+ }
+ catch (const std::exception& e) {
+ throw std::runtime_error("Failed to check if file is empty: " + std::string(e.what()));
+ }
+}
+
+bool NahidaProject::FileHelper::CreateDirectory(const std::string& dirPath) {
+ try {
+ return fs::create_directories(dirPath);
+ }
+ catch (const std::exception& e) {
+ throw std::runtime_error("Failed to create directory: " + std::string(e.what()));
+ }
+}
+
+std::vector NahidaProject::FileHelper::ListFiles(const std::string& dirPath) {
+ try {
+ std::vector files;
+ if (fs::exists(dirPath) && fs::is_directory(dirPath)) {
+ for (const auto& entry : fs::directory_iterator(dirPath)) {
+ if (entry.is_regular_file()) {
+ files.push_back(entry.path().string());
+ }
+ }
+ }
+ return files;
+ }
+ catch (const std::exception& e) {
+ throw std::runtime_error("Failed to list files: " + std::string(e.what()));
+ }
+}
+
+void NahidaProject::FileHelper::ValidateFilename() const {
+ if (fileName.empty()) {
+ throw std::runtime_error("Filename is empty");
+ }
+}
diff --git a/NahidaProject.Generic/Sources/FileHelper.h b/NahidaProject.Generic/Sources/FileHelper.h
new file mode 100644
index 0000000000000000000000000000000000000000..71c5b6d595fe2e238fe53beb09ca8fcc45639066
--- /dev/null
+++ b/NahidaProject.Generic/Sources/FileHelper.h
@@ -0,0 +1,53 @@
+#pragma once
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+namespace fs = std::filesystem;
+
+namespace NahidaProject {
+ class FileHelper {
+ private:
+ std::string fileName;
+ mutable std::fstream file;
+
+ public:
+ explicit FileHelper(const std::string& fileName = "") : fileName(fileName) {}
+
+ ~FileHelper() {
+ if (file.is_open()) {
+ file.close();
+ }
+ }
+
+ void SetFilename(const std::string& fileName);
+ std::string GetFilename() const;
+ bool Exists() const;
+ bool CreateFile() const;
+ void WriteText(const std::string& content) const;
+ void AppendText(const std::string& content) const;
+ void WriteLines(const std::vector& lines) const;
+ std::string ReadWholeText() const;
+ std::vector ReadLines() const;
+ std::vector ReadBinary() const;
+ void WriteBinary(const std::vector& data) const;
+ static bool CopyFile(const std::string& source, const std::string& destination);
+ static bool MoveFile(const std::string& source, const std::string& destination);
+ static bool DeleteFile(const std::string& fname);
+ std::uintmax_t GetFileSize() const;
+ std::time_t GetLastModified() const;
+ void ClearFile() const;
+ bool IsEmpty() const;
+ static bool CreateDirectory(const std::string& dirPath);
+ static std::vector ListFiles(const std::string& dirPath);
+ private:
+ void ValidateFilename() const;
+ };
+}
diff --git a/NahidaProject.Generic/Sources/HTTPRequest.cpp b/NahidaProject.Generic/Sources/HTTPRequest.cpp
deleted file mode 100644
index bc272d29308f1257cfcc5e9c9dce577b65a7f1b8..0000000000000000000000000000000000000000
--- a/NahidaProject.Generic/Sources/HTTPRequest.cpp
+++ /dev/null
@@ -1,252 +0,0 @@
-/*
-Copyright (c) 2025 HeZongLun
-NahidaProject is licensed under Mulan PSL v2.
-You can use this software according to the terms and conditions of the Mulan
-PSL v2.
-You may obtain a copy of Mulan PSL v2 at:
- http://license.coscl.org.cn/MulanPSL2
-THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY
-KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
-NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
-See the Mulan PSL v2 for more details.
-*/
-
-#include "HTTPRequest.h"
-
-NahidaProject::HTTPRequest::Implement::TCPSocket::TCPSocket() noexcept : rawSocket(-1) {
-
-}
-
-NahidaProject::HTTPRequest::Implement::TCPSocket::~TCPSocket() noexcept {
- Close();
-}
-
-bool NahidaProject::HTTPRequest::Implement::TCPSocket::Connect(const std::string& host, std::uint16_t port) noexcept {
- rawSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
- if (rawSocket == INVALID_SOCKET) {
- return false;
- }
-
- if (u_long mode = 1; ioctlsocket(rawSocket, FIONBIO, &mode) == SOCKET_ERROR) {
- return false;
- }
-
- addrinfo hints = {}, * res;
- hints.ai_family = AF_INET;
- hints.ai_socktype = SOCK_STREAM;
-
- int result = getaddrinfo(host.c_str(), std::to_string(port).c_str(), &hints, &res);
- if (result != 0) {
- return false;
- }
-
- result = ::connect(rawSocket, res->ai_addr, res->ai_addrlen);
- freeaddrinfo(res);
-
- if (result == SOCKET_ERROR) {
- if (WSAGetLastError() == WSAEWOULDBLOCK) {
- return true;
- }
- return false;
- }
-
- return true;
-}
-
-bool NahidaProject::HTTPRequest::Implement::TCPSocket::Close() noexcept {
- if (rawSocket != INVALID_SOCKET && closesocket(rawSocket) != SOCKET_ERROR) {
- return true;
- }
- return false;
-}
-
-bool NahidaProject::HTTPRequest::Implement::TCPSocket::Send(const std::byte* data, std::size_t len) noexcept {
- if (::send(rawSocket, reinterpret_cast(data), len, 0) != SOCKET_ERROR) {
- return true;
- }
-
- return false;
-}
-
-bool NahidaProject::HTTPRequest::Implement::TCPSocket::Receive(std::byte* data, std::size_t len) noexcept {
- int bytesReceived = recv(rawSocket, reinterpret_cast(data), len, 0);
-
- if (bytesReceived != SOCKET_ERROR && bytesReceived != 0) {
- return true;
- }
- else {
- return false;
- }
-}
-
-NahidaProject::HTTPRequest::AsyncHTTPRequest::AsyncHTTPRequest() noexcept : responseBuffer(4096), readyToReceive(false) {
-
-}
-
-std::string NahidaProject::HTTPRequest::AsyncHTTPRequest::GetHeader(const std::string& name) const noexcept {
- auto it = headers.find(name);
- if (it != headers.end()) {
- return it->second;
- }
- return "";
-}
-
-void NahidaProject::HTTPRequest::AsyncHTTPRequest::SetHeader(const std::string& name, const std::string& value) noexcept {
- headers[name] = value;
-}
-
-void NahidaProject::HTTPRequest::AsyncHTTPRequest::RemoveHeader(const std::string& name) noexcept {
- headers.erase(name);
-}
-
-void NahidaProject::HTTPRequest::AsyncHTTPRequest::ClearHeaders() noexcept {
- headers.clear();
-}
-
-void NahidaProject::HTTPRequest::AsyncHTTPRequest::Get(std::string_view url, const CallBack& callback) noexcept {
- std::string host;
- std::string path;
-
- if (!ParseURL(url, host, path)) {
- callback("Invalid URL");
- return;
- }
-
- if (!socket.Connect(host, 80)) {
- callback("Connection failed");
- return;
- }
-
- request = "GET " + path + " HTTP/1.1\r\n" + "Host: " + host + "\r\n";
-
- for (const auto& header : headers) {
- request += header.first + ": " + header.second + "\r\n";
- }
-
- request += "Connection: close\r\n\r\n";
-
- response.clear();
-
- readyToReceive = false;
- this->callback = callback;
-}
-
-void NahidaProject::HTTPRequest::AsyncHTTPRequest::Post(std::string_view url, CallBack callback, ContentType content_type, const std::string& data) noexcept {
- std::string host;
- std::string path;
-
- if (!ParseURL(url, host, path)) {
- callback("Invalid URL");
- return;
- }
-
- if (!socket.Connect(host, 80)) {
- callback("Connection failed");
- return;
- }
-
- std::string content_type_str;
- switch (content_type) {
- case ContentType::formUrlencoded:
- content_type_str = "application/x-www-form-urlencoded";
- break;
- case ContentType::json:
- content_type_str = "application/json";
- break;
- case ContentType::xml:
- content_type_str = "application/xml";
- break;
- case ContentType::text:
- content_type_str = "text/plain";
- break;
- default:
- content_type_str = "application/octet-stream";
- break;
- }
-
- std::string encoded_data = (content_type == ContentType::formUrlencoded) ? URLEncode(data) : data;
-
- request = "POST " + path + " HTTP/1.1\r\n" + "Host: " + host + "\r\n" + "Content-Type: " + content_type_str + "\r\n" + "Content-Length: " + std::to_string(encoded_data.length()) + "\r\n";
-
- for (const auto& header : headers) {
- request += header.first + ": " + header.second + "\r\n";
- }
-
- request += "Connection: close\r\n\r\n";
- request += encoded_data;
-
- response.clear();
-
- readyToReceive = false;
- this->callback = callback;
-}
-
-bool NahidaProject::HTTPRequest::AsyncHTTPRequest::Server() noexcept {
- if (readyToReceive) {
- if (socket.Receive(responseBuffer.data(), responseBuffer.size())) {
- response.append(reinterpret_cast(responseBuffer.data()));
- responseBuffer.assign(responseBuffer.size(), std::byte{ 0 });
-
- packetTimeout = std::chrono::system_clock::now();
- packetTimeout += std::chrono::milliseconds(50);
- }
-
- if (std::chrono::system_clock::now() >= packetTimeout) {
- callback((response.empty()) ? "No data" : response);
- socket.Close();
- return true;
- }
- }
- else if (socket.Send(reinterpret_cast(request.c_str()), request.size())) {
- packetTimeout = std::chrono::system_clock::now();
- packetTimeout += std::chrono::milliseconds(5000);
-
- readyToReceive = true;
- }
-
- return false;
-}
-
-bool NahidaProject::HTTPRequest::AsyncHTTPRequest::ParseURL(std::string_view url, std::string& host, std::string& path) const noexcept {
- size_t protocol_pos = url.find("://");
- if (protocol_pos == std::string::npos) {
- return false;
- }
-
- protocol_pos += 3;
- size_t host_end = url.find('/', protocol_pos);
- if (host_end == std::string::npos) {
- host = url.substr(protocol_pos);
- path = "/";
- }
- else {
- host = url.substr(protocol_pos, host_end - protocol_pos);
- path = url.substr(host_end);
- }
-
- return true;
-}
-
-std::string NahidaProject::HTTPRequest::AsyncHTTPRequest::URLEncode(const std::string& data) const noexcept {
- std::string encoded_data;
- const std::string safe_chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-_.~";
- for (char c : data) {
- if (std::isalnum(c) || safe_chars.find(c) != std::string::npos) {
- encoded_data.push_back(c);
- }
- else {
- encoded_data += "%" + ToHEX(static_cast(c));
- }
- }
- return encoded_data;
-}
-
-std::string NahidaProject::HTTPRequest::AsyncHTTPRequest::ToHEX(unsigned char c) const noexcept {
- const char* hex_digits = "0123456789ABCDEF";
- std::string result;
-
- result.push_back(hex_digits[c >> 4]);
- result.push_back(hex_digits[c & 15]);
-
- return result;
-}
\ No newline at end of file
diff --git a/NahidaProject.Generic/Sources/HTTPRequest.h b/NahidaProject.Generic/Sources/HTTPRequest.h
deleted file mode 100644
index 1dd90d65528952bafc01610fc6c2576902112609..0000000000000000000000000000000000000000
--- a/NahidaProject.Generic/Sources/HTTPRequest.h
+++ /dev/null
@@ -1,88 +0,0 @@
-/*
-Copyright (c) 2025 HeZongLun
-NahidaProject is licensed under Mulan PSL v2.
-You can use this software according to the terms and conditions of the Mulan
-PSL v2.
-You may obtain a copy of Mulan PSL v2 at:
- http://license.coscl.org.cn/MulanPSL2
-THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY
-KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
-NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
-See the Mulan PSL v2 for more details.
-*/
-
-#include
-#include
-
-#pragma comment(lib, "ws2_32.lib")
-
-#include
-#include
-#include
-#include
-#include
-#include
-
-namespace NahidaProject {
- namespace HTTPRequest {
- namespace Implement {
- static class __declspec(dllexport) Initializer {
- public:
- Initializer() noexcept {
- WSADATA wsaData;
- if (WSAStartup(MAKEWORD(1, 1), &wsaData)) {
-
- }
- }
- ~Initializer() noexcept {
- WSACleanup();
- }
- } Initializer;
-
- class __declspec(dllexport) TCPSocket {
- public:
- TCPSocket() noexcept;
- ~TCPSocket() noexcept;
-
- bool Connect(const std::string& host, std::uint16_t port) noexcept;
- bool Close() noexcept;
-
- bool Send(const std::byte* data, std::size_t len) noexcept;
- bool Receive(std::byte* data, std::size_t len) noexcept;
- private:
- SOCKET rawSocket;
- };
- }
-
- enum class __declspec(dllexport) ContentType { formUrlencoded, json, xml, text };
-
- class __declspec(dllexport) AsyncHTTPRequest {
- public:
- using CallBack = std::function;
-
- AsyncHTTPRequest() noexcept;
-
- std::string GetHeader(const std::string& name) const noexcept;
- void SetHeader(const std::string& name, const std::string& value) noexcept;
- void RemoveHeader(const std::string& name) noexcept;
- void ClearHeaders() noexcept;
- void Get(std::string_view url, const CallBack& callback) noexcept;
- void Post(std::string_view url, CallBack callback, ContentType content_type, const std::string& data) noexcept;
- bool Server() noexcept;
-
- private:
- Implement::TCPSocket socket;
- std::chrono::time_point packetTimeout;
- std::vector responseBuffer;
- std::string request;
- std::string response;
- std::unordered_map headers;
- bool readyToReceive;
- CallBack callback;
-
- bool ParseURL(std::string_view url, std::string& host, std::string& path) const noexcept;
- std::string URLEncode(const std::string& data) const noexcept;
- std::string ToHEX(unsigned char c) const noexcept;
- };
- }
-}
\ No newline at end of file
diff --git a/NahidaProject.Generic/Sources/SnowFlake.cpp b/NahidaProject.Generic/Sources/SnowFlake.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..d1663ebacec6425ba98ca26d0b0ea82614b07c7c
--- /dev/null
+++ b/NahidaProject.Generic/Sources/SnowFlake.cpp
@@ -0,0 +1,39 @@
+#include "SnowFlake.h"
+
+int64_t NahidaProject::SnowFlake::GetCurrentTimeStamp() {
+ auto now = std::chrono::duration_cast(std::chrono::steady_clock::now().time_since_epoch()).count();
+ return now;
+}
+
+int64_t NahidaProject::SnowFlake::WaitNextMillis(int64_t lastTimeStamp) {
+ int64_t timeStamp;
+ do {
+ timeStamp = GetCurrentTimeStamp();
+ } while (timeStamp <= lastTimeStamp);
+ return timeStamp;
+}
+
+int64_t NahidaProject::SnowFlake::NextId() {
+ std::lock_guard lock(mutex);
+
+ int64_t timeStamp = GetCurrentTimeStamp();
+
+ if (timeStamp < lastTimeStamp) {
+ throw std::runtime_error("Clock moved backwards. Refusing to generate id.");
+ }
+
+ if (timeStamp == lastTimeStamp) {
+ sequence = (sequence + 1) & kMaxSequence;
+ if (sequence == 0) {
+ timeStamp = WaitNextMillis(lastTimeStamp);
+ }
+ }
+ else {
+ sequence = 0;
+ }
+
+ lastTimeStamp = timeStamp;
+
+ int64_t id = ((timeStamp - kEpoch) << kTimestampShift) | (machineId << kMachineIdShift) | sequence;
+ return id;
+}
diff --git a/NahidaProject.Generic/Sources/SnowFlake.h b/NahidaProject.Generic/Sources/SnowFlake.h
new file mode 100644
index 0000000000000000000000000000000000000000..fa24b29adb817f813eee6d314a558e92b18b75a1
--- /dev/null
+++ b/NahidaProject.Generic/Sources/SnowFlake.h
@@ -0,0 +1,37 @@
+#include
+#include
+#include
+#include
+
+namespace NahidaProject {
+ class SnowFlake {
+ private:
+ static constexpr int64_t kEpoch = 1640995200000LL;
+ static constexpr int kTimestampBits = 41;
+ static constexpr int kMachineIdBits = 10;
+ static constexpr int kSequenceBits = 12;
+
+ static constexpr int64_t kMaxMachineId = (1LL << kMachineIdBits) - 1;
+ static constexpr int64_t kMaxSequence = (1LL << kSequenceBits) - 1;
+
+ static constexpr int kMachineIdShift = kSequenceBits;
+ static constexpr int kTimestampShift = kSequenceBits + kMachineIdBits;
+
+ int64_t machineId;
+ int64_t lastTimeStamp;
+ int64_t sequence;
+ std::mutex mutex;
+
+ int64_t GetCurrentTimeStamp();
+ int64_t WaitNextMillis(int64_t lastTimeStamp);
+
+ public:
+ SnowFlake(int64_t machineId) : machineId(machineId), lastTimeStamp(0), sequence(0) {
+ if (machineId < 0 || machineId > kMaxMachineId) {
+ throw std::invalid_argument("Machine ID out of range.");
+ }
+ }
+
+ int64_t NextId();
+ };
+}
\ No newline at end of file
diff --git a/NahidaProject.Generic/Sources/StringValidator.cpp b/NahidaProject.Generic/Sources/StringValidator.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..7f334a0f31a9d57fb5a160850213e48c935f793e
--- /dev/null
+++ b/NahidaProject.Generic/Sources/StringValidator.cpp
@@ -0,0 +1,148 @@
+#include "StringValidator.h"
+
+const std::regex& NahidaProject::StringValidator::GetRegex(ValidationType type) const {
+ auto it = compiledRegex.find(type);
+ if (it == compiledRegex.end()) {
+ auto patternIt = regexPatterns.find(type);
+ if (patternIt != regexPatterns.end()) {
+ compiledRegex[type] = std::regex(patternIt->second);
+ it = compiledRegex.find(type);
+ }
+ else {
+ throw std::invalid_argument("Unknown validation type");
+ }
+ }
+ return it->second;
+}
+
+NahidaProject::StringValidator::ValidationResult NahidaProject::StringValidator::ValidateRegex(const std::string& str, const std::string& pattern) const {
+ try {
+ std::regex re(pattern);
+ return ValidationResult(std::regex_match(str, re), "Pattern mismatch");
+ }
+ catch (const std::regex_error& e) {
+ return ValidationResult(false, "Invalid regex pattern: " + std::string(e.what()));
+ }
+}
+
+NahidaProject::StringValidator::ValidationResult NahidaProject::StringValidator::Validate(const std::string& str, ValidationType type) const {
+ if (str.empty()) {
+ return ValidationResult(false, "String is empty");
+ }
+
+ try {
+ const std::regex& re = GetRegex(type);
+ bool match = std::regex_match(str, re);
+
+ std::string typeName;
+ switch (type) {
+ case ValidationType::EMAIL: typeName = "email"; break;
+ case ValidationType::PHONE: typeName = "phone"; break;
+ case ValidationType::URL: typeName = "URL"; break;
+ case ValidationType::IP_ADDRESS: typeName = "IP address"; break;
+ case ValidationType::MAC_ADDRESS: typeName = "MAC address"; break;
+ case ValidationType::DATE: typeName = "date"; break;
+ case ValidationType::TIME: typeName = "time"; break;
+ case ValidationType::DATETIME: typeName = "datetime"; break;
+ case ValidationType::NUMBER: typeName = "number"; break;
+ case ValidationType::INTEGER: typeName = "integer"; break;
+ case ValidationType::FLOAT: typeName = "float"; break;
+ case ValidationType::HEX: typeName = "hexadecimal"; break;
+ case ValidationType::BASE64: typeName = "base64"; break;
+ case ValidationType::UUID: typeName = "UUID"; break;
+ case ValidationType::CREDIT_CARD: typeName = "credit card"; break;
+ case ValidationType::POSTAL_CODE: typeName = "postal code"; break;
+ default: typeName = "custom format"; break;
+ }
+
+ return ValidationResult(match, match ? "" : "Invalid " + typeName + " format");
+ }
+ catch (const std::exception& e) {
+ return ValidationResult(false, "Validation error: " + std::string(e.what()));
+ }
+}
+
+NahidaProject::StringValidator::ValidationResult NahidaProject::StringValidator::ValidateLength(const std::string& str, size_t minimumLength, size_t maximumLength) const {
+ size_t length = str.length();
+ if (length < minimumLength) {
+ return ValidationResult(false, "String too short, minimum length is " + std::to_string(minimumLength));
+ }
+ if (length > maximumLength) {
+ return ValidationResult(false, "String too long, maximum length is " + std::to_string(maximumLength));
+ }
+ return ValidationResult(true);
+}
+
+NahidaProject::StringValidator::ValidationResult NahidaProject::StringValidator::ValidateCharset(const std::string& str, const std::string& allowedChars) const {
+ for (char c : str) {
+ if (allowedChars.find(c) == std::string::npos) {
+ return ValidationResult(false, "Invalid character: " + std::string(1, c));
+ }
+ }
+ return ValidationResult(true);
+}
+
+NahidaProject::StringValidator::ValidationResult NahidaProject::StringValidator::ValidateCustom(const std::string& str, std::function validator, const std::string& errorMessage) const {
+ try {
+ bool result = validator(str);
+ return ValidationResult(result, result ? "" : errorMessage);
+ }
+ catch (const std::exception& e) {
+ return ValidationResult(false, "Custom validation error: " + std::string(e.what()));
+ }
+}
+
+NahidaProject::StringValidator::ValidationResult NahidaProject::StringValidator::ValidateMultiple(const std::string& str, const std::vector>& validations) const {
+ for (const auto& validation : validations) {
+ ValidationResult result = Validate(str, validation.first);
+ if (!result.isValid) {
+ return ValidationResult(false, validation.second.empty() ? result.errorMessage : validation.second);
+ }
+ }
+ return ValidationResult(true);
+}
+
+void NahidaProject::StringValidator::AddCustomPattern(ValidationType type, const std::string& pattern) {
+ regexPatterns[type] = pattern;
+ compiledRegex.erase(type);
+}
+
+NahidaProject::StringValidator::ValidationResult NahidaProject::StringValidator::ValidateEmail(const std::string& email) const {
+ return Validate(email, ValidationType::EMAIL);
+}
+
+NahidaProject::StringValidator::ValidationResult NahidaProject::StringValidator::ValidatePhone(const std::string& phone) const {
+ return Validate(phone, ValidationType::PHONE);
+}
+
+NahidaProject::StringValidator::ValidationResult NahidaProject::StringValidator::ValidateURL(const std::string& url) const {
+ return Validate(url, ValidationType::URL);
+}
+
+NahidaProject::StringValidator::ValidationResult NahidaProject::StringValidator::ValidateIPAddress(const std::string& ip) const {
+ return Validate(ip, ValidationType::IP_ADDRESS);
+}
+
+NahidaProject::StringValidator::ValidationResult NahidaProject::StringValidator::ValidateDate(const std::string& date) const {
+ return Validate(date, ValidationType::DATE);
+}
+
+NahidaProject::StringValidator::ValidationResult NahidaProject::StringValidator::ValidateNumber(const std::string& number) const {
+ return Validate(number, ValidationType::NUMBER);
+}
+
+NahidaProject::StringValidator::ValidationResult NahidaProject::StringValidator::ValidateInteger(const std::string& integer) const {
+ return Validate(integer, ValidationType::INTEGER);
+}
+
+NahidaProject::StringValidator::ValidationResult NahidaProject::StringValidator::ValidateHex(const std::string& hex) const {
+ return Validate(hex, ValidationType::HEX);
+}
+
+std::vector> NahidaProject::StringValidator::BatchValidate(const std::vector>& items) const {
+ std::vector> results;
+ for (const auto& item : items) {
+ results.emplace_back(item.first, Validate(item.first, item.second));
+ }
+ return results;
+}
diff --git a/NahidaProject.Generic/Sources/StringValidator.h b/NahidaProject.Generic/Sources/StringValidator.h
new file mode 100644
index 0000000000000000000000000000000000000000..05361716d3b9295b405f81926291d83431120048
--- /dev/null
+++ b/NahidaProject.Generic/Sources/StringValidator.h
@@ -0,0 +1,83 @@
+#include
+#include
+#include
+#include
+#include
+#include