Skip to content
Draft
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
130 changes: 47 additions & 83 deletions main.cpp
Original file line number Diff line number Diff line change
@@ -1,16 +1,20 @@
// C library headers
#include <curl/curl.h>
#include <string.h>

#include <unordered_set>
#include <iostream>
// C++ standard library headers
#include <fstream>
#include <map>
#include <iostream>
#include <string>
#include <vector>

// Third-party headers
#include <nlohmann/json.hpp>

using json = nlohmann::json;

// used to have a dynamic string
typedef struct Response
// Structure to hold dynamic string response from CURL
struct Response
{
char *string;
size_t size;
Expand All @@ -23,12 +27,9 @@ struct TestCaseResponse
};

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<std::string, std::string> GetParamName(const std::string &param);
void CreateJSON(json *response, const TestCaseResponse &testCases);

int main()
Expand All @@ -53,7 +54,7 @@ int main()
}

Response response;
response.string = (char *)malloc(1);
response.string = static_cast<char*>(malloc(1));
response.size = 0;

// Set options for the HTTP request
Expand Down Expand Up @@ -87,7 +88,7 @@ int main()
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_chunk);

// Address of response string is passed in write_chunk as userData
curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&response);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, static_cast<void*>(&response));

// Perform the HTTP request
result = curl_easy_perform(curl);
Expand All @@ -105,43 +106,40 @@ int main()
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)
// Returns number of bytes in the chunk
// data: pointer to block of data received in this chunk
// nmemb: number of bytes in the block of data
// userData: pointer to where the response string is stored
size_t write_chunk(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);
Response *response = static_cast<Response*>(userData);
// Allocate more space for chunk that was received
// response->size is size of existing mem and real_size is the size received and +1 accounts for null
char *ptr = static_cast<char*>(realloc(response->string, response->size + real_size + 1));

if (ptr == nullptr)
{
std::cerr << "Problem reallocating space for chunk recieved" << std::endl;
std::cerr << "Problem reallocating space for chunk received" << std::endl;
return 0;
}
// set response string to the new (larger) memory address
// Set response string to the new (larger) memory address
response->string = ptr;
// append new porition onto existing string
// Append new portion onto existing string
memcpy(&(response->string[response->size]), data, real_size);
// update strings size
// Update string size
response->size += real_size;
// append null character
// Append null character
response->string[response->size] = '\0';
return real_size;
}

/**
* 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.
* Formats the JSON response from LeetCode's GraphQL API.
* Processes tags: title, content, difficulty, topicTags, hints
* Extracts test cases from content and creates output JSON file.
*/
void formatResponse(char *response)
{
Expand Down Expand Up @@ -195,15 +193,15 @@ void formatResponse(char *response)
}
}

// check for <code> tag
// Converts HTML content to plain text by removing HTML tags and converting HTML entities
std::string FormatHTMLToString(const std::string &response)
{
int i = 0;
std::string result = "";

while (i < response.length())
{
// check for HTML elements
// Check for HTML elements
if (response[i] == '<')
{
while (response[i] != '>')
Expand All @@ -214,7 +212,7 @@ std::string FormatHTMLToString(const std::string &response)
continue;
}

// check for &lt; (<) , &gt (>);
// Check for &lt; (<), &gt (>)
if (i < response.length() - 4 && (response.substr(i, 4) == "&lt;" || response.substr(i, 4) == "&gt;"))
{
std::string expression = response.substr(i, 4);
Expand All @@ -230,30 +228,30 @@ std::string FormatHTMLToString(const std::string &response)
continue;
}

// check for &amp (&)
// Check for &amp (&)
if (i < response.length() - 5 && (response.substr(i, 5) == "&amp;"))
{
result += "&";
i += 5;
continue;
}

// check for &#39;s
// Check for &#39;s
if (i < response.length() - 6 && response.substr(i, 6) == "&#39;s")
{
i += 6;
continue;
}

// check for &nbsp; tags
// Check for &nbsp; tags
if (i < response.length() - 6 && response.substr(i, 6) == "&nbsp;")
{
i += 6;
continue;
}

// check for multiple whitespace characters
// want to keep 1 where there are multiple
// Check for multiple whitespace characters
// Keep only one where there are multiple
if (response[i] == '\n')
{
result += "\n";
Expand Down Expand Up @@ -282,9 +280,9 @@ 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 to extract input parameters and expected outputs.
* @returns TestCaseResponse containing test cases and their parameters
*/
TestCaseResponse GetTestCases(const std::string &content)
{
Expand All @@ -307,7 +305,7 @@ TestCaseResponse GetTestCases(const std::string &content)
int j = -1;
while (i < content.length() - 7 && content.substr(i, 7) != "\nOutput")
{
// check if new param is being searched
// Check if new param is being searched
if (i < content.length() - 1 && (content[i] == ',' && content[i + 1] == ' '))
{
tests.testCaseParams.push_back({paramName, paramRes});
Expand All @@ -317,7 +315,7 @@ TestCaseResponse GetTestCases(const std::string &content)
i++;
continue;
}
// now looking for paramResult so set j (flag for where = is)
// Now looking for paramResult so set j (flag for where = is)
if (content[i] == '=')
{
j = i;
Expand All @@ -339,7 +337,6 @@ TestCaseResponse GetTestCases(const std::string &content)
{
tests.testCaseParams.push_back({paramName, paramRes});
}
// std::cout << paramName << " " << paramRes << std::endl;
}

if (i <= content.length() - 6 && content.substr(i, 6) == "Output")
Expand Down Expand Up @@ -371,7 +368,7 @@ TestCaseResponse GetTestCases(const std::string &content)

void CreateJSON(json *response, const TestCaseResponse &tests)
{
// filter out invalid characters from title
// Filter out invalid characters from title
std::string title = (*response)["title"];
const std::string invalid_chars = "\\/:*?\"<>|";
for (char c : invalid_chars)
Expand All @@ -382,33 +379,31 @@ void CreateJSON(json *response, const TestCaseResponse &tests)

std::ofstream outputJSON;
outputJSON.open(jsonName);
// should have to create the file so always should open
// Should be able to create the file
if (!outputJSON.is_open())
{
std::cerr << "Error creating output file for JSON response" << std::endl;
return;
}

outputJSON << "{\n";
// iterates through json response inserting key and value as pair into output file
// Iterate through JSON response inserting key and value pairs into output file
for (auto it = (*response).begin(); it != (*response).end(); ++it)
{
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++)
{
// start inserting new object into array inside json file
// Start inserting new object into array inside JSON file
outputJSON << "{\n";

std::string expectedResult = tests.testCases[i]; // testcase expected outputs
std::string expectedResult = tests.testCases[i]; // Test case expected outputs
outputJSON << "\"expectedResult\": " << "\"" << expectedResult << "\",\n";

int numParams = tests.testCaseParams.size() / tests.testCases.size();
Expand All @@ -425,7 +420,7 @@ void CreateJSON(json *response, const TestCaseResponse &tests)
}
}

// if i is at the end then we need to close off the obj
// If at the end, close off the object without comma
if (i == size - 1)
{
outputJSON << "}\n";
Expand All @@ -440,35 +435,4 @@ void CreateJSON(json *response, const TestCaseResponse &tests)

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<std::string, std::string> GetParamName(const std::string &param)
{
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};
}