From cb007bab5882d86f02491458eab54b6617971875 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Sat, 8 Nov 2025 21:56:46 +0000 Subject: [PATCH] Refactor: Improve code structure and readability This commit refactors the code to improve its structure and readability. It introduces new functions, renames existing ones for clarity, and updates data structures to use modern C++ features. The changes aim to make the codebase more maintainable and easier to understand. Co-authored-by: jacegenereux --- main.cpp | 291 +++++++++++++++++++++++-------------------------------- 1 file changed, 120 insertions(+), 171 deletions(-) diff --git a/main.cpp b/main.cpp index c3fbad7..9c7c8d1 100644 --- a/main.cpp +++ b/main.cpp @@ -1,19 +1,20 @@ #include -#include +#include -#include #include #include -#include +#include +#include +#include +#include #include using json = nlohmann::json; -// used to have a dynamic string -typedef struct Response +// CURL callback data structure +struct Response { - char *string; - size_t size; + std::string data; }; struct TestCaseResponse @@ -22,14 +23,11 @@ struct TestCaseResponse std::vector> testCaseParams; }; -size_t write_chunk(void *data, size_t size, size_t nmemb, void *userData); - -void formatResponse(char *response); -std::string FormatHTMLToString(const std::string &response); -TestCaseResponse GetTestCases(const std::string &content); - -std::pair GetParamName(const std::string ¶m); -void CreateJSON(json *response, const TestCaseResponse &testCases); +size_t writeChunk(void *data, size_t size, size_t nmemb, void *userData); +void formatResponse(const std::string &response); +std::string formatHTMLToString(const std::string &response); +TestCaseResponse getTestCases(const std::string &content); +void createJSON(const json &response, const TestCaseResponse &testCases); int main() { @@ -37,24 +35,17 @@ int main() std::cout << "Enter Leetcode question name: " << std::endl; std::cin >> questionName; - CURL *curl; - CURLcode result; - // Initialize CURL - curl = curl_easy_init(); + CURL *curl = curl_easy_init(); if (curl == nullptr) { std::cerr << "HTTP REQUEST FAILED: curl_easy_init() failed!" << std::endl; return -1; } - else - { - std::cout << "Curl initialized successfully!" << std::endl; - } + + std::cout << "Curl initialized successfully!" << std::endl; Response response; - response.string = (char *)malloc(1); - response.size = 0; // Set options for the HTTP request curl_easy_setopt(curl, CURLOPT_URL, @@ -80,70 +71,55 @@ int main() curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); /** * WriteFunction allows for specifying a callback function - * Curl_easy_perfrom will call this function repeatedly - * Each time it is called the pointer is passed to a new chunk of response - * string + * curl_easy_perform will call this function repeatedly + * Each time it is called the pointer is passed to a new chunk of response data */ - curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_chunk); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writeChunk); - // Address of response string is passed in write_chunk as userData - curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&response); + // Address of response struct is passed in writeChunk as userData + curl_easy_setopt(curl, CURLOPT_WRITEDATA, static_cast(&response)); // Perform the HTTP request - result = curl_easy_perform(curl); + CURLcode result = curl_easy_perform(curl); if (result != CURLE_OK) { std::cerr << "Error: " << curl_easy_strerror(result) << std::endl; curl_easy_cleanup(curl); + curl_slist_free_all(headers); return -1; } - formatResponse(response.string); - free(response.string); + formatResponse(response.data); + // Cleanup curl_easy_cleanup(curl); + curl_slist_free_all(headers); return 0; } -// returns number of bytes in the chunk -// data is set to a ptr that points to block of data recieved in this chunk -// nmemb is the number of bytes in the block of data -// userData points to what we want (points to where the response string is stored) -size_t write_chunk(void *data, size_t size, size_t nmemb, void *userData) +/** + * CURL callback function to write received data + * @param data Pointer to the received data chunk + * @param size Size of each element (typically 1) + * @param nmemb Number of elements + * @param userData Pointer to Response structure + * @return Number of bytes processed + */ +size_t writeChunk(void *data, size_t size, size_t nmemb, void *userData) { - // size is always 1 - size_t real_size = size * nmemb; - - Response *response = (Response *)userData; - // allocate more space for chunk that was recieved - // response->size is size of existing mem and real_size is the size recieved and +1 accounts for null - char *ptr = (char *)realloc(response->string, response->size + real_size + 1); - - if (ptr == nullptr) - { - std::cerr << "Problem reallocating space for chunk recieved" << std::endl; - return 0; - } - // set response string to the new (larger) memory address - response->string = ptr; - // append new porition onto existing string - memcpy(&(response->string[response->size]), data, real_size); - // update strings size - response->size += real_size; - // append null character - response->string[response->size] = '\0'; - return real_size; + size_t realSize = size * nmemb; + Response *response = static_cast(userData); + + response->data.append(static_cast(data), realSize); + return realSize; } /** - * Returns a map containing the following tags stored as keys - * and their description as their value. - * - * title content difficulty topicTags { name } hints - * - * Assumes json response will use the tags in the given order above. + * Processes the JSON response from LeetCode GraphQL API + * Extracts and formats: title, content, difficulty, topicTags, and hints + * @param response Raw JSON response string */ -void formatResponse(char *response) +void formatResponse(const std::string &response) { std::vector currentTags = {"title", "content", "difficulty", "topicTags", "hints"}; @@ -172,21 +148,21 @@ void formatResponse(char *response) { continue; } - question[tag][0] = FormatHTMLToString(question[tag][0]); + question[tag][0] = formatHTMLToString(question[tag][0]); continue; } if (question.contains(tag)) { - question[tag] = FormatHTMLToString(question[tag]); - // Get testcases from given content + question[tag] = formatHTMLToString(question[tag]); + // Get test cases from given content if (tag == "content") { - testCases = GetTestCases(question[tag]); + testCases = getTestCases(question[tag]); } } } - CreateJSON(&question, testCases); + createJSON(question, testCases); } catch (json::parse_error &e) { @@ -195,11 +171,17 @@ void formatResponse(char *response) } } -// check for tag -std::string FormatHTMLToString(const std::string &response) +/** + * Converts HTML entities and removes HTML tags from string + * Handles: , <, >, &, 's,   + * @param response HTML string to format + * @return Cleaned string without HTML + */ +std::string formatHTMLToString(const std::string &response) { - int i = 0; - std::string result = ""; + std::string result; + result.reserve(response.length()); + size_t i = 0; while (i < response.length()) { @@ -214,39 +196,41 @@ std::string FormatHTMLToString(const std::string &response) continue; } - // check for < (<) , > (>); - if (i < response.length() - 4 && (response.substr(i, 4) == "<" || response.substr(i, 4) == ">")) + // check for < (<) , > (>) + if (i + 4 <= response.length()) { std::string expression = response.substr(i, 4); if (expression == "<") { result += "<"; + i += 4; + continue; } else if (expression == ">") { result += ">"; + i += 4; + continue; } - i += 4; - continue; } - // check for & (&) - if (i < response.length() - 5 && (response.substr(i, 5) == "&")) + // check for & (&) + if (i + 5 <= response.length() && response.substr(i, 5) == "&") { result += "&"; i += 5; continue; } - // check for 's - if (i < response.length() - 6 && response.substr(i, 6) == "'s") + // check for 's (possessive apostrophe) + if (i + 6 <= response.length() && response.substr(i, 6) == "'s") { i += 6; continue; } - // check for   tags - if (i < response.length() - 6 && response.substr(i, 6) == " ") + // check for   (non-breaking space) + if (i + 6 <= response.length() && response.substr(i, 6) == " ") { i += 6; continue; @@ -282,35 +266,35 @@ std::string FormatHTMLToString(const std::string &response) } /** - * Basic test cases given by leetcode are given in a string of the form. Example case & output. - * Should always be at least 2 test cases given. - * @returns array of oxpected outputs for the test cases. + * Extracts test cases from LeetCode problem content + * Parses Example sections containing Input and Output + * @param content Problem content string + * @return TestCaseResponse containing test cases and parameters */ -TestCaseResponse GetTestCases(const std::string &content) +TestCaseResponse getTestCases(const std::string &content) { TestCaseResponse tests; - int i = 0; + size_t i = 0; while (i < content.length()) { - if (i < content.length() - 7 && content.substr(i, 7) == "Example") + if (i + 7 <= content.length() && content.substr(i, 7) == "Example") { i += 7; while (i < content.length()) { - if (i <= content.length() - 6 && content.substr(i, 6) == "Input:") + if (i + 6 <= content.length() && content.substr(i, 6) == "Input:") { i += 6; - std::string input = ""; - std::string paramName = ""; - std::string paramRes = ""; + std::string paramName; + std::string paramRes; int j = -1; - while (i < content.length() - 7 && content.substr(i, 7) != "\nOutput") + while (i + 7 <= content.length() && content.substr(i, 7) != "\nOutput") { // check if new param is being searched if (i < content.length() - 1 && (content[i] == ',' && content[i + 1] == ' ')) { - tests.testCaseParams.push_back({paramName, paramRes}); + tests.testCaseParams.emplace_back(paramName, paramRes); paramName = ""; paramRes = ""; j = -1; @@ -335,17 +319,16 @@ TestCaseResponse GetTestCases(const std::string &content) } i++; } - if (paramName.length() != 0 && paramRes.length() != 0) + if (!paramName.empty() && !paramRes.empty()) { - tests.testCaseParams.push_back({paramName, paramRes}); + tests.testCaseParams.emplace_back(paramName, paramRes); } - // std::cout << paramName << " " << paramRes << std::endl; } - if (i <= content.length() - 6 && content.substr(i, 6) == "Output") + if (i + 6 <= content.length() && content.substr(i, 6) == "Output") { i += 6; - std::string testCase = ""; + std::string testCase; while (i < content.length() && content[i] != '\n') { if (content[i] != ' ' && content[i] != ':') @@ -369,20 +352,23 @@ TestCaseResponse GetTestCases(const std::string &content) return tests; } -void CreateJSON(json *response, const TestCaseResponse &tests) +/** + * Creates a JSON output file with the problem details and test cases + * @param response JSON object containing problem data + * @param tests TestCaseResponse with test cases and parameters + */ +void createJSON(const json &response, const TestCaseResponse &tests) { - // filter out invalid characters from title - std::string title = (*response)["title"]; - const std::string invalid_chars = "\\/:*?\"<>|"; - for (char c : invalid_chars) + // Filter out invalid characters from title for filename + std::string title = response["title"]; + const std::string invalidChars = "\\/:*?\"<>|"; + for (char c : invalidChars) { std::replace(title.begin(), title.end(), c, '_'); } std::string jsonName = "../../../Questions/" + title + ".txt"; - std::ofstream outputJSON; - outputJSON.open(jsonName); - // should have to create the file so always should open + std::ofstream outputJSON(jsonName); if (!outputJSON.is_open()) { std::cerr << "Error creating output file for JSON response" << std::endl; @@ -390,85 +376,48 @@ void CreateJSON(json *response, const TestCaseResponse &tests) } outputJSON << "{\n"; - // iterates through json response inserting key and value as pair into output file - for (auto it = (*response).begin(); it != (*response).end(); ++it) + // Iterate through JSON response inserting key and value pairs + for (auto it = response.begin(); it != response.end(); ++it) { - outputJSON << "\"" << it.key() << "\"" << ": " << it.value() << ',' << "\n"; + outputJSON << "\"" << it.key() << "\"" << ": " << it.value() << ",\n"; } - // handle situation where testCases might not generate - - // Insert testcases + // Insert test cases outputJSON << "\"testCases\"" << ": [" << "\n"; - int j = 0; - int size = tests.testCases.size(); - for (int i = 0; i < size; i++) + size_t j = 0; + size_t size = tests.testCases.size(); + for (size_t i = 0; i < size; i++) { - // start inserting new object into array inside json file + // Start inserting new object into array outputJSON << "{\n"; - std::string expectedResult = tests.testCases[i]; // testcase expected outputs - outputJSON << "\"expectedResult\": " << "\"" << expectedResult << "\",\n"; + const std::string &expectedResult = tests.testCases[i]; + outputJSON << "\"expectedResult\": \"" << expectedResult << "\",\n"; - int numParams = tests.testCaseParams.size() / tests.testCases.size(); - for (int x = 0; x < numParams; x++) + size_t numParams = tests.testCases.empty() ? 0 : tests.testCaseParams.size() / tests.testCases.size(); + for (size_t x = 0; x < numParams; x++) { - std::pair fixedParam = tests.testCaseParams[j++]; - if (x == numParams - 1) + const auto &fixedParam = tests.testCaseParams[j++]; + outputJSON << "\"" << fixedParam.first << "\": \"" << fixedParam.second << "\""; + if (x < numParams - 1) { - outputJSON << "\"" << fixedParam.first << "\": " << "\"" << fixedParam.second << "\"\n"; - } - else - { - outputJSON << "\"" << fixedParam.first << "\": " << "\"" << fixedParam.second << "\",\n"; + outputJSON << ","; } + outputJSON << "\n"; } - // if i is at the end then we need to close off the obj - if (i == size - 1) - { - outputJSON << "}\n"; - } - else + // Close the test case object + outputJSON << "}"; + if (i < size - 1) { - outputJSON << "},\n"; + outputJSON << ","; } + outputJSON << "\n"; } outputJSON << "]\n"; outputJSON << "}"; outputJSON.close(); -} - -/** - * params are taken from the json as a string containing 'paramName'='param' - * This function splits the paramName and param seperately to label them in the output JSON easier. - * (the problem function calls explicility used by the users will contain the same paramNames so makes using them easier as well) - */ -std::pair GetParamName(const std::string ¶m) -{ - std::string paramName = ""; - std::string paramResult = ""; - bool nameParsed = false; - for (int i = 0; i < param.length(); i++) - { - - if (param[i] == '=') - { - nameParsed = true; - continue; - } - - if (param[i] != ' ' && !nameParsed) - { - paramName += param[i]; - } - else if (param[i] != ' ' && nameParsed) - { - paramResult += param[i]; - } - } - return {paramName, paramResult}; } \ No newline at end of file